AndroidKotlinDevelopment android-kotlin-development

使用 Kotlin 构建遵循现代架构模式和最佳实践的原生 Android 应用,涵盖 MVVM 架构、Jetpack 库、Compose UI、Retrofit API 调用、Room 本地存储等关键技术。

移动开发 0 次安装 0 次浏览 更新于 3/3/2026

Android Kotlin 开发

概览

使用 Kotlin 构建健壮的原生 Android 应用,采用现代架构模式、Jetpack 库和 Compose 进行声明式 UI 开发。

何时使用

  • 创建遵循最佳实践的原生 Android 应用
  • 使用 Kotlin 进行类型安全开发
  • 实现 Jetpack 的 MVVM 架构
  • 使用 Jetpack Compose 构建现代 UI
  • 集成 Android 平台 API

指令

1. 模型与 API 服务

// 模型
data class User(
  val id: String,
  val name: String,
  val email: String,
  val avatarUrl: String? = null
)

data class Item(
  val id: String,
  val title: String,
  val description: String,
  val imageUrl: String? = null,
  val price: Double
)

// 使用 Retrofit 的 API 服务
interface ApiService {
  @GET("/users/{id}")
  suspend fun getUser(@Path("id") userId: String): User

  @PUT("/users/{id}")
  suspend fun updateUser(
    @Path("id") userId: String,
    @Body user: User
  ): User

  @GET("/items")
  suspend fun getItems(@Query("filter") filter: String = "all"): List<Item>

  @POST("/items")
  suspend fun createItem(@Body item: Item): Item
}

// 网络客户端设置
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
  @Provides
  @Singleton
  fun provideRetrofit(): Retrofit {
    val httpClient = OkHttpClient.Builder()
      .addInterceptor { chain ->
        val original = chain.request()
        val requestBuilder = original.newBuilder()

        val token = PreferencesManager.getToken()
        if (token.isNotEmpty()) {
          requestBuilder.addHeader("Authorization", "Bearer $token")
        }

        requestBuilder.addHeader("Content-Type", "application/json")
        chain.proceed(requestBuilder.build())
      }
      .connectTimeout(30, TimeUnit.SECONDS)
      .readTimeout(30, TimeUnit.SECONDS)
      .build()

    return Retrofit.Builder()
      .baseUrl("https://api.example.com")
      .client(httpClient)
      .addConverterFactory(GsonConverterFactory.create())
      .build()
  }

  @Provides
  @Singleton
  fun provideApiService(retrofit: Retrofit): ApiService {
    return retrofit.create(ApiService::class.java)
  }
}

2. Jetpack MVVM ViewModels

@HiltViewModel
class UserViewModel @Inject constructor(
  private val apiService: ApiService
) : ViewModel() {
  private val _user = MutableStateFlow<User?>(null)
  val user: StateFlow<User?> = _user.asStateFlow()

  private val _isLoading = MutableStateFlow(false)
  val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

  private val _errorMessage = MutableStateFlow<String?>(null)
  val errorMessage: StateFlow<String?> = _errorMessage.asStateFlow()

  fun fetchUser(userId: String) {
    viewModelScope.launch {
      _isLoading.value = true
      _errorMessage.value = null

      try {
        val user = apiService.getUser(userId)
        _user.value = user
      } catch (e: Exception) {
        _errorMessage.value = e.message ?: "Unknown error"
      } finally {
        _isLoading.value = false
      }
    }
  }

  fun logout() {
    _user.value = null
  }
}

@HiltViewModel
class ItemsViewModel @Inject constructor(
  private val apiService: ApiService
) : ViewModel() {
  private val _items = MutableStateFlow<List<Item>>(emptyList())
  val items: StateFlow<List<Item>> = _items.asStateFlow()

  private val _isLoading = MutableStateFlow(false)
  val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

  fun fetchItems(filter: String = "all") {
    viewModelScope.launch {
      _isLoading.value = true
      try {
        val items = apiService.getItems(filter)
        _items.value = items
      } catch (e: Exception) {
        println("Error fetching items: ${e.message}")
      } finally {
        _isLoading.value = false
      }
    }
  }

  fun addItem(item: Item) {
    viewModelScope.launch {
      try {
        val created = apiService.createItem(item)
        _items.value = _items.value + created
      } catch (e: Exception) {
        println("Error creating item: ${e.message}")
      }
    }
  }
}

3. Jetpack Compose UI

@Composable
fun MainScreen() {
  val navController = rememberNavController()

  NavHost(navController = navController, startDestination = "home") {
    composable("home") { HomeScreen(navController) }
    composable("profile") { ProfileScreen(navController) }
    composable("details/{itemId}") { backStackEntry ->
      val itemId = backStackEntry.arguments?.getString("itemId") ?: return@composable
      DetailsScreen(itemId = itemId, navController = navController)
    }
  }
}

@Composable
fun HomeScreen(navController: NavController) {
  val viewModel: ItemsViewModel = hiltViewModel()
  val items by viewModel.items.collectAsState()
  val isLoading by viewModel.isLoading.collectAsState()

  LaunchedEffect(Unit) {
    viewModel.fetchItems()
  }

  Scaffold(
    topBar = { TopAppBar(title = { Text("Items") }) }
  ) { paddingValues ->
    if (isLoading) {
      Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        CircularProgressIndicator()
      }
    } else {
      LazyColumn(
        modifier = Modifier
          .padding(paddingValues)
          .fillMaxSize(),
        contentPadding = PaddingValues(8.dp)
      ) {
        items(items) { item ->
          ItemCard(
            item = item,
            onClick = { navController.navigate("details/${item.id}") }
          )
        }
      }
    }
  }
}

@Composable
fun ItemCard(item: Item, onClick: () -> Unit) {
  Card(
    modifier = Modifier
      .fillMaxWidth()
      .padding(8.dp)
      .clickable { onClick() }
  ) {
    Row(modifier = Modifier.padding(16.dp)) {
      Column(modifier = Modifier.weight(1f)) {
        Text(text = item.title, style = MaterialTheme.typography.headlineSmall)
        Text(text = item.description, style = MaterialTheme.typography.bodyMedium)
        Text(text = "$${item.price}", style = MaterialTheme.typography.bodySmall)
      }
      Icon(imageVector = Icons.Default.ArrowForward, contentDescription = null)
    }
  }
}

@Composable
fun ProfileScreen(navController: NavController) {
  val viewModel: UserViewModel = hiltViewModel()
  val user by viewModel.user.collectAsState()
  val isLoading by viewModel.isLoading.collectAsState()

  LaunchedEffect(Unit) {
    viewModel.fetchUser("current-user")
  }

  Scaffold(
    topBar = { TopAppBar(title = { Text("Profile") }) }
  ) { paddingValues ->
    if (isLoading) {
      Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        CircularProgressIndicator()
      }
    } else if (user != null) {
      Column(
        modifier = Modifier
          .padding(paddingValues)
          .fillMaxSize()
          .padding(16.dp)
      ) {
        Text(text = user!!.name, style = MaterialTheme.typography.headlineMedium)
        Text(text = user!!.email, style = MaterialTheme.typography.bodyMedium)

        Spacer(modifier = Modifier.height(24.dp))

        Button(
          onClick = { viewModel.logout() },
          modifier = Modifier.fillMaxWidth()
        ) {
          Text("Logout")
        }
      }
    }
  }
}

@Composable
fun DetailsScreen(itemId: String, navController: NavController) {
  Scaffold(
    topBar = {
      TopAppBar(
        title = { Text("Details") },
        navigationIcon = {
          IconButton(onClick = { navController.popBackStack() }) {
            Icon(Icons.Default.ArrowBack, contentDescription = "Back")
          }
        }
      )
    }
  ) { paddingValues ->
    Column(
      modifier = Modifier
        .padding(paddingValues)
        .fillMaxSize()
        .padding(16.dp),
      horizontalAlignment = Alignment.CenterHorizontally,
      verticalArrangement = Arrangement.Center
    ) {
      Text("Item ID: $itemId", style = MaterialTheme.typography.headlineSmall)
    }
  }
}

最佳实践

✅ DO

  • 使用 Kotlin 开发所有新的 Android 代码
  • 使用 Jetpack 库实现 MVVM
  • 使用 Jetpack Compose 进行 UI 开发
  • 利用协程进行异步操作
  • 使用 Room 进行本地数据持久化
  • 实施适当的错误处理
  • 使用 Hilt 进行依赖注入
  • 使用 StateFlow 进行响应式状态管理
  • 在多种设备类型上进行测试
  • 遵循 Android 设计指南

❌ DON’T

  • 不要在 SharedPreferences 中存储令牌
  • 不要在主线程上进行网络调用
  • 忽略生命周期管理
  • 跳过空安全检查
  • 硬编码字符串和资源
  • 忽略配置更改
  • 在代码中存储密码
  • 未经设备测试就部署
  • 使用过时的 API
  • 累积内存泄漏