引言
Android作为全球最流行的移动操作系统,拥有超过25亿活跃设备。对于开发者而言,掌握Android编程不仅意味着能够开发应用,更是进入移动开发领域的关键技能。本文将从Android开发的基础知识开始,逐步深入到高级主题,通过详细的实例分析和实战案例,帮助读者从入门走向精通。同时,我们还将探讨常见问题及其解决方案,确保读者在实际开发中能够应对各种挑战。
第一部分:Android开发基础
1.1 Android开发环境搭建
在开始Android编程之前,首先需要搭建开发环境。Android Studio是Google官方推荐的集成开发环境(IDE),它提供了代码编辑、调试、性能分析等全方位的工具支持。
步骤:
- 下载并安装Android Studio:访问Android开发者官网下载最新版本。
- 配置SDK:安装完成后,启动Android Studio,按照向导配置Android SDK。建议选择最新的稳定版本。
- 创建第一个项目:选择”Start a new Android Studio project”,选择”Empty Activity”模板,填写项目名称、包名、保存位置等信息,点击”Finish”。
示例代码:
// MainActivity.kt
package com.example.myfirstapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
1.2 Android应用基本结构
Android应用通常由以下组件构成:
- Activity:用户交互界面,一个Activity通常对应一个屏幕。
- Fragment:Activity的一部分,可以复用和组合。
- Service:后台运行的服务,不提供用户界面。
- BroadcastReceiver:接收和响应系统或应用广播。
- ContentProvider:管理应用间共享的数据。
示例:创建一个简单的Activity
// MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.textView)
textView.text = "Hello, Android!"
}
}
1.3 布局文件与UI组件
Android使用XML文件定义用户界面。常见的UI组件包括TextView、Button、EditText、ImageView等。
示例:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, Android!" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me" />
</LinearLayout>
1.4 数据存储
Android提供多种数据存储方式:
- SharedPreferences:存储键值对,适合保存少量数据。
- SQLite数据库:存储结构化数据,适合复杂查询。
- 文件存储:存储大文件或非结构化数据。
- 网络存储:通过API与服务器交互。
示例:使用SharedPreferences存储数据
// 保存数据
val sharedPref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
with(sharedPref.edit()) {
putString("username", "JohnDoe")
apply()
}
// 读取数据
val username = sharedPref.getString("username", "default")
第二部分:中级Android开发
2.1 RecyclerView与列表展示
RecyclerView是Android中用于展示大量数据的高效组件,它通过视图复用机制优化性能。
步骤:
- 添加依赖:在build.gradle文件中添加RecyclerView依赖。
- 创建布局文件:定义列表项的布局。
- 创建适配器:继承RecyclerView.Adapter,绑定数据到视图。
- 在Activity中使用RecyclerView。
示例代码:
// 1. 添加依赖(build.gradle)
dependencies {
implementation 'androidx.recyclerview:recyclerview:1.3.2'
}
// 2. 创建列表项布局(item_layout.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher_background" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Item Name" />
</LinearLayout>
// 3. 创建适配器
class MyAdapter(private val dataList: List<String>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.text = dataList[position]
}
override fun getItemCount(): Int = dataList.size
}
// 4. 在Activity中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val dataList = listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
val adapter = MyAdapter(dataList)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
}
}
2.2 网络请求与数据解析
在现代应用中,与服务器交互是必不可少的。Retrofit是Android中常用的网络请求库,结合Gson可以轻松实现数据解析。
步骤:
- 添加依赖:在build.gradle中添加Retrofit和Gson依赖。
- 定义数据模型:创建数据类。
- 定义API接口:使用注解定义请求。
- 创建Retrofit实例并发起请求。
示例代码:
// 1. 添加依赖
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.10.1'
}
// 2. 定义数据模型
data class User(
val id: Int,
val name: String,
val email: String
)
// 3. 定义API接口
interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: Int): Response<User>
}
// 4. 创建Retrofit实例并发起请求
class MainActivity : AppCompatActivity() {
private lateinit var retrofit: Retrofit
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val apiService = retrofit.create(ApiService::class.java)
// 使用协程发起网络请求
lifecycleScope.launch {
try {
val response = apiService.getUser(1)
if (response.isSuccessful) {
val user = response.body()
// 更新UI
findViewById<TextView>(R.id.textView).text = user?.name ?: "Unknown"
}
} catch (e: Exception) {
// 处理错误
findViewById<TextView>(R.id.textView).text = "Error: ${e.message}"
}
}
}
}
2.3 数据库操作(Room)
Room是Google推荐的SQLite抽象层,它提供了编译时验证,简化了数据库操作。
步骤:
- 添加依赖:在build.gradle中添加Room依赖。
- 定义实体类:使用@Entity注解。
- 定义DAO接口:使用@Dao注解。
- 定义数据库类:使用@Database注解。
- 在应用中使用。
示例代码:
// 1. 添加依赖
dependencies {
implementation 'androidx.room:room-runtime:2.6.1'
kapt 'androidx.room:room-compiler:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1'
}
// 2. 定义实体类
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String,
val email: String
)
// 3. 定义DAO接口
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User)
@Query("SELECT * FROM users")
suspend fun getAllUsers(): List<User>
}
// 4. 定义数据库类
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build()
INSTANCE = instance
instance
}
}
}
}
// 5. 在应用中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val db = AppDatabase.getDatabase(this)
val userDao = db.userDao()
// 插入数据
lifecycleScope.launch {
userDao.insert(User(name = "John", email = "john@example.com"))
val users = userDao.getAllUsers()
// 更新UI显示用户列表
}
}
}
第三部分:高级Android开发
3.1 Jetpack Compose
Jetpack Compose是Android的现代UI工具包,使用Kotlin声明式编程,简化了UI开发。
示例:创建一个简单的Compose应用
// 1. 添加依赖(build.gradle)
dependencies {
implementation 'androidx.activity:activity-compose:1.8.2'
implementation 'androidx.compose.ui:ui:1.6.4'
implementation 'androidx.compose.material3:material3:1.2.1'
implementation 'androidx.compose.ui:ui-tooling-preview:1.6.4'
implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.7.0'
}
// 2. 创建Compose函数
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
@Composable
fun MyApp() {
MaterialTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Greeting("Android")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { /* Handle click */ }) {
Text("Click Me")
}
}
}
}
}
// 3. 在Activity中使用
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp()
}
}
}
3.2 依赖注入(Dagger Hilt)
依赖注入(Dependency Injection)是一种设计模式,用于管理对象之间的依赖关系。Dagger Hilt是Android推荐的依赖注入框架。
步骤:
- 添加依赖:在build.gradle中添加Hilt依赖。
- 定义模块:使用@Module和@Provides注解。
- 定义组件:使用@HiltAndroidApp和@HiltAndroidComponent注解。
- 在类中使用:使用@Inject注解。
示例代码:
// 1. 添加依赖
plugins {
id 'com.google.dagger.hilt.android'
}
dependencies {
implementation 'com.google.dagger:hilt-android:2.50'
kapt 'com.google.dagger:hilt-compiler:2.50'
}
// 2. 定义模块
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
// 3. 定义组件
@HiltAndroidApp
class MyApplication : Application()
// 4. 在类中使用
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 使用apiService发起网络请求
}
}
3.3 性能优化
性能优化是Android开发中的重要环节。以下是一些常见的优化策略:
- 布局优化:减少嵌套层级,使用ConstraintLayout,避免过度绘制。
- 内存优化:及时释放资源,避免内存泄漏,使用LeakCanary检测内存泄漏。
- 网络优化:使用缓存,减少网络请求次数,使用OkHttp的拦截器。
- 电池优化:使用WorkManager处理后台任务,避免频繁唤醒设备。
示例:使用LeakCanary检测内存泄漏
// 1. 添加依赖
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
// 2. 在Application类中初始化
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
if (LeakCanary.isInAnalyzerProcess(this)) {
return
}
LeakCanary.install(this)
}
}
第四部分:实战案例详解
4.1 案例一:天气预报应用
需求分析:
- 用户输入城市名称,显示当前天气信息。
- 使用网络API获取天气数据。
- 使用RecyclerView展示未来几天的天气预报。
实现步骤:
- UI设计:使用ConstraintLayout设计主界面,包含输入框、按钮和显示区域。
- 网络请求:使用Retrofit调用天气API(如OpenWeatherMap)。
- 数据解析:使用Gson解析JSON数据。
- 数据展示:使用RecyclerView展示天气预报列表。
关键代码:
// 1. 定义数据模型
data class WeatherResponse(
val main: Main,
val weather: List<Weather>,
val name: String
)
data class Main(
val temp: Double,
val humidity: Int
)
data class Weather(
val description: String,
val icon: String
)
// 2. 定义API接口
interface WeatherApiService {
@GET("weather")
suspend fun getWeather(
@Query("q") city: String,
@Query("appid") apiKey: String
): Response<WeatherResponse>
}
// 3. 在Activity中使用
class WeatherActivity : AppCompatActivity() {
private lateinit var retrofit: Retrofit
private lateinit var weatherApiService: WeatherApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_weather)
retrofit = Retrofit.Builder()
.baseUrl("https://api.openweathermap.org/data/2.5/")
.addConverterFactory(GsonConverterFactory.create())
.build()
weatherApiService = retrofit.create(WeatherApiService::class.java)
val cityInput = findViewById<EditText>(R.id.cityInput)
val searchButton = findViewById<Button>(R.id.searchButton)
val weatherInfo = findViewById<TextView>(R.id.weatherInfo)
searchButton.setOnClickListener {
val city = cityInput.text.toString()
if (city.isNotEmpty()) {
lifecycleScope.launch {
try {
val response = weatherApiService.getWeather(city, "YOUR_API_KEY")
if (response.isSuccessful) {
val weather = response.body()
weatherInfo.text = "City: ${weather?.name}\n" +
"Temperature: ${weather?.main?.temp}°C\n" +
"Description: ${weather?.weather?.get(0)?.description}"
} else {
weatherInfo.text = "Error: ${response.message()}"
}
} catch (e: Exception) {
weatherInfo.text = "Error: ${e.message}"
}
}
}
}
}
}
4.2 案例二:待办事项应用(Todo List)
需求分析:
- 用户可以添加、删除、标记完成待办事项。
- 数据持久化存储(使用Room数据库)。
- 支持搜索和过滤功能。
实现步骤:
- UI设计:使用RecyclerView展示待办事项列表,添加输入框和按钮。
- 数据模型:定义TodoItem实体类。
- 数据库操作:使用Room进行增删改查。
- 适配器:自定义适配器处理点击事件。
关键代码:
// 1. 定义实体类
@Entity(tableName = "todo_items")
data class TodoItem(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val title: String,
val isCompleted: Boolean = false,
val createdAt: Long = System.currentTimeMillis()
)
// 2. 定义DAO
@Dao
interface TodoDao {
@Insert
suspend fun insert(todo: TodoItem)
@Update
suspend fun update(todo: TodoItem)
@Delete
suspend fun delete(todo: TodoItem)
@Query("SELECT * FROM todo_items ORDER BY createdAt DESC")
suspend fun getAllTodos(): List<TodoItem>
@Query("SELECT * FROM todo_items WHERE title LIKE '%' || :query || '%'")
suspend fun searchTodos(query: String): List<TodoItem>
}
// 3. 定义适配器
class TodoAdapter(
private val onItemClicked: (TodoItem) -> Unit,
private val onItemDeleted: (TodoItem) -> Unit
) : RecyclerView.Adapter<TodoAdapter.ViewHolder>() {
private var todoList = emptyList<TodoItem>()
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val title: TextView = itemView.findViewById(R.id.todoTitle)
val checkbox: CheckBox = itemView.findViewById(R.id.todoCheckbox)
val deleteButton: ImageButton = itemView.findViewById(R.id.deleteButton)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_todo, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val todo = todoList[position]
holder.title.text = todo.title
holder.checkbox.isChecked = todo.isCompleted
holder.itemView.setOnClickListener {
onItemClicked(todo)
}
holder.deleteButton.setOnClickListener {
onItemDeleted(todo)
}
}
override fun getItemCount(): Int = todoList.size
fun submitList(list: List<TodoItem>) {
todoList = list
notifyDataSetChanged()
}
}
// 4. 在Activity中使用
class TodoActivity : AppCompatActivity() {
private lateinit var todoDao: TodoDao
private lateinit var adapter: TodoAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_todo)
val db = AppDatabase.getDatabase(this)
todoDao = db.todoDao()
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val inputEditText = findViewById<EditText>(R.id.inputEditText)
val addButton = findViewById<Button>(R.id.addButton)
val searchEditText = findViewById<EditText>(R.id.searchEditText)
adapter = TodoAdapter(
onItemClicked = { todo ->
// 更新完成状态
lifecycleScope.launch {
todoDao.update(todo.copy(isCompleted = !todo.isCompleted))
loadTodos()
}
},
onItemDeleted = { todo ->
lifecycleScope.launch {
todoDao.delete(todo)
loadTodos()
}
}
)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
addButton.setOnClickListener {
val title = inputEditText.text.toString()
if (title.isNotEmpty()) {
lifecycleScope.launch {
todoDao.insert(TodoItem(title = title))
inputEditText.text.clear()
loadTodos()
}
}
}
searchEditText.addTextChangedListener { text ->
lifecycleScope.launch {
val query = text.toString()
val todos = if (query.isEmpty()) {
todoDao.getAllTodos()
} else {
todoDao.searchTodos(query)
}
adapter.submitList(todos)
}
}
// 初始加载
loadTodos()
}
private suspend fun loadTodos() {
val todos = todoDao.getAllTodos()
adapter.submitList(todos)
}
}
第五部分:常见问题解决方案
5.1 内存泄漏
问题描述: 内存泄漏是指对象不再被使用,但由于被其他对象引用而无法被垃圾回收,导致内存占用不断增加。
解决方案:
- 避免静态引用:不要将Activity或Context作为静态变量引用。
- 使用弱引用:对于需要长期持有的对象,使用WeakReference。
- 及时释放资源:在onDestroy()中释放资源,如取消网络请求、关闭数据库连接等。
- 使用LeakCanary:集成LeakCanary检测内存泄漏。
示例:
// 错误示例:静态引用导致内存泄漏
class MyActivity : AppCompatActivity() {
companion object {
private var sInstance: MyActivity? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sInstance = this // 错误:静态引用Activity
}
override fun onDestroy() {
super.onDestroy()
sInstance = null // 正确:在onDestroy中释放引用
}
}
// 正确示例:使用弱引用
class MyActivity : AppCompatActivity() {
companion object {
private var sInstance: WeakReference<MyActivity>? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sInstance = WeakReference(this)
}
}
5.2 网络请求失败
问题描述: 网络请求失败可能由多种原因引起,如网络连接问题、服务器错误、请求参数错误等。
解决方案:
- 检查网络权限:确保在AndroidManifest.xml中添加了网络权限。
- 使用OkHttp拦截器:添加日志拦截器,查看请求和响应详情。
- 错误处理:使用try-catch捕获异常,提供用户友好的错误提示。
- 重试机制:对于临时性错误,可以添加重试逻辑。
示例:
// 1. 添加网络权限(AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET" />
// 2. 使用OkHttp拦截器
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.addInterceptor { chain ->
val request = chain.request()
val response = try {
chain.proceed(request)
} catch (e: Exception) {
// 处理异常
throw e
}
response
}
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
// 3. 错误处理
lifecycleScope.launch {
try {
val response = apiService.getUser(1)
if (response.isSuccessful) {
// 成功处理
} else {
// 处理服务器错误
val errorBody = response.errorBody()?.string()
// 显示错误信息
}
} catch (e: SocketTimeoutException) {
// 超时错误
showError("请求超时,请稍后重试")
} catch (e: IOException) {
// 网络错误
showError("网络连接失败,请检查网络设置")
} catch (e: Exception) {
// 其他错误
showError("发生未知错误:${e.message}")
}
}
5.3 UI卡顿
问题描述: UI卡顿通常由主线程执行耗时操作引起,如网络请求、数据库查询、复杂计算等。
解决方案:
- 使用协程:将耗时操作放在后台线程,使用协程简化异步编程。
- 避免在主线程进行I/O操作:使用withContext切换线程。
- 优化布局:减少嵌套层级,使用ConstraintLayout,避免过度绘制。
- 使用性能分析工具:使用Android Profiler分析性能瓶颈。
示例:
// 错误示例:在主线程执行耗时操作
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 错误:在主线程执行网络请求
val response = retrofit.create(ApiService::class.java).getUser(1).execute()
// 这会导致UI卡顿
}
}
// 正确示例:使用协程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
// 在后台线程执行网络请求
val response = withContext(Dispatchers.IO) {
retrofit.create(ApiService::class.java).getUser(1).execute()
}
// 在主线程更新UI
if (response.isSuccessful) {
val user = response.body()
findViewById<TextView>(R.id.textView).text = user?.name ?: "Unknown"
}
}
}
}
5.4 权限管理
问题描述: Android 6.0(API 23)及以上版本需要动态请求权限,而不是在安装时一次性授予。
解决方案:
- 检查权限:使用ContextCompat.checkSelfPermission()检查权限状态。
- 请求权限:使用ActivityCompat.requestPermissions()请求权限。
- 处理结果:在onRequestPermissionsResult()中处理用户响应。
- 解释权限用途:在请求前向用户解释为什么需要该权限。
示例:
// 1. 在AndroidManifest.xml中声明权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
// 2. 动态请求权限
class MainActivity : AppCompatActivity() {
private val CAMERA_PERMISSION = Manifest.permission.CAMERA
private val STORAGE_PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val cameraButton = findViewById<Button>(R.id.cameraButton)
cameraButton.setOnClickListener {
requestPermissions()
}
}
private fun requestPermissions() {
val permissions = arrayOf(CAMERA_PERMISSION, STORAGE_PERMISSION)
val permissionList = permissions.filter {
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
}.toTypedArray()
if (permissionList.isNotEmpty()) {
ActivityCompat.requestPermissions(this, permissionList, 100)
} else {
// 权限已授予,执行相关操作
openCamera()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 100) {
var allGranted = true
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false
break
}
}
if (allGranted) {
openCamera()
} else {
// 解释权限重要性并引导用户到设置
showPermissionExplanation()
}
}
}
private fun openCamera() {
// 打开相机的逻辑
}
private fun showPermissionExplanation() {
AlertDialog.Builder(this)
.setTitle("权限请求")
.setMessage("需要相机和存储权限才能正常使用此功能")
.setPositiveButton("去设置") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.fromParts("package", packageName, null)
startActivity(intent)
}
.setNegativeButton("取消", null)
.show()
}
}
第六部分:最佳实践与进阶建议
6.1 代码组织与架构
推荐架构:MVVM(Model-View-ViewModel)
- Model:数据层,负责数据获取和存储。
- View:UI层,负责显示数据和用户交互。
- ViewModel:业务逻辑层,连接View和Model,处理UI相关逻辑。
示例:MVVM架构实现
// 1. Model层(数据源)
class UserRepository(private val apiService: ApiService) {
suspend fun getUser(id: Int): User {
return apiService.getUser(id)
}
}
// 2. ViewModel层
class UserViewModel(private val repository: UserRepository) : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> = _user
fun loadUser(id: Int) {
viewModelScope.launch {
try {
val user = repository.getUser(id)
_user.value = user
} catch (e: Exception) {
// 处理错误
}
}
}
}
// 3. View层(Activity/Fragment)
class UserActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
val repository = UserRepository(retrofit.create(ApiService::class.java))
viewModel = ViewModelProvider(this, ViewModelProvider.Factory.from(repository))[UserViewModel::class.java]
viewModel.user.observe(this) { user ->
findViewById<TextView>(R.id.textView).text = user?.name ?: "Unknown"
}
viewModel.loadUser(1)
}
}
6.2 测试策略
单元测试: 使用JUnit和Mockito测试ViewModel和Repository。 集成测试: 使用Espresso测试UI交互。 端到端测试: 使用UI Automator测试多个应用交互。
示例:单元测试
// 1. 添加测试依赖
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.10.0'
testImplementation 'androidx.arch.core:core-testing:2.2.0'
}
// 2. 编写测试
class UserViewModelTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
@Test
fun `loadUser should update LiveData`() = runTest {
// Arrange
val mockRepository = mock(UserRepository::class.java)
val expectedUser = User(1, "John", "john@example.com")
`when`(mockRepository.getUser(1)).thenReturn(expectedUser)
val viewModel = UserViewModel(mockRepository)
// Act
viewModel.loadUser(1)
// Assert
val user = viewModel.user.getOrAwaitValue()
assertEquals(expectedUser, user)
}
}
6.3 持续学习与资源推荐
- 官方文档:Android开发者官网
- 在线课程:Udacity的Android开发纳米学位,Google的Android开发者课程。
- 开源项目:GitHub上的优秀Android项目,如Now in Android、Sunflower。
- 社区:Stack Overflow、Reddit的r/androiddev、Android开发者博客。
结语
Android开发是一个不断演进的领域,从基础的Activity和Fragment到现代的Jetpack Compose和Kotlin协程,开发者需要持续学习和实践。本文通过详细的实例分析和实战案例,涵盖了从入门到精通的各个阶段,并提供了常见问题的解决方案。希望这些内容能够帮助你在Android开发的道路上不断进步,开发出优秀的应用。
记住,实践是学习的最佳方式。尝试构建自己的项目,参与开源社区,不断挑战自己。祝你在Android开发的旅程中取得成功!
