Android架构示例:Jetpack Compose与MVVM实践
项目标题与描述
Android Architecture Samples 是Google官方维护的架构示范项目,通过TODO应用展示多种Android开发架构模式。当前分支重点演示:
- 采用Jetpack Compose构建的现代化UI体系
- 符合单一Activity原则的导航架构
- 完备的MVVM分层设计与响应式编程实现
- 适用于生产环境的测试方案和依赖注入
功能特性
核心架构
- 🏗️ 分层架构:清晰划分表示层(Compose+ViewModel)、领域层和数据层(Room+远程数据)
- 🌐 响应式UI:使用Kotlin Flow实现数据驱动的界面更新
- :puzzle_piece: 模块化设计:通过Hilt实现依赖注入,支持功能解耦
开发支持
- :artist_palette: 双风味配置:提供
mock
和prod
产品风味,简化开发测试流程 - :magnifying_glass_tilted_left: 完整测试套件:包含单元测试、集成测试和端到端测试
- :high_voltage: 协程应用:全异步操作采用Kotlin协程实现
技术栈亮点
dependencies {
implementation("androidx.compose.ui:ui") // Jetpack Compose
implementation("androidx.navigation:navigation-compose") // 导航组件
implementation("androidx.room:room-ktx") // Room数据库
implementation("com.google.dagger:hilt-android") // 依赖注入
}
安装指南
环境要求
- Android Studio Giraffe以上版本
- JDK 17+
- Android SDK API 34
获取代码
git clone git@github.com:android/architecture-samples.git
cd architecture-samples
构建配置
- 在Android Studio中打开项目根目录
- 选择构建风味:
mock
:使用内存数据库的调试版本prod
:包含真实数据库的生产配置
- 执行Gradle同步
使用说明
基础功能实现
// 数据层Repository示例
class DefaultTasksRepository(
private val tasksRemoteDataSource: TasksDataSource,
private val tasksLocalDataSource: TasksDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {
override fun getTasks(): Flow<List<Task>> {
return tasksLocalDataSource.getTasks()
}
}
ViewModel典型实现
class TasksViewModel(
private val tasksRepository: TasksRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(TasksUiState())
val uiState: StateFlow<TasksUiState> = _uiState.asStateFlow()
fun refresh() {
viewModelScope.launch {
tasksRepository.refreshTasks()
_uiState.update { it.copy(loading = false) }
}
}
}
Compose界面集成
@Composable
fun TasksScreen(
viewModel: TasksViewModel = hiltViewModel(),
onTaskClick: (Task) -> Unit
) {
val uiState by viewModel.uiState.collectAsState()
LazyColumn {
items(uiState.tasks) { task ->
TaskItem(task, onTaskClick)
}
}
}
核心代码
导航系统实现
// 导航图配置
@Composable
fun TodoNavGraph(
navController: NavHostController = rememberNavController()
) {
NavHost(
navController = navController,
startDestination = Screen.Tasks.route
) {
composable(Screen.Tasks.route) {
TasksScreen { task ->
navController.navigate("${Screen.TaskDetail.route}/${task.id}")
}
}
composable(
route = "${Screen.TaskDetail.route}/{taskId}",
arguments = listOf(navArgument("taskId") { type = NavType.StringType })
) { backStackEntry ->
val taskId = backStackEntry.arguments?.getString("taskId")!!
TaskDetailScreen(taskId = taskId)
}
}
}
数据库访问层
// Room数据库定义
@Database(entities = [Task::class], version = 1)
@TypeConverters(Converters::class)
abstract class ToDoDatabase : RoomDatabase() {
abstract fun taskDao(): TasksDao
}
// DAO接口
@Dao
interface TasksDao {
@Query("SELECT * FROM tasks")
fun observeTasks(): Flow<List<Task>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertTask(task: Task)
}
测试方案示例
// ViewModel测试
@HiltAndroidTest
class TasksViewModelTest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@Inject
lateinit var repository: FakeTestRepository
@Before
fun setup() {
hiltRule.inject()
}
@Test
fun loadingTasks_emitsSuccessUiState() = runTest {
val viewModel = TasksViewModel(repository)
viewModel.refresh()
val state = viewModel.uiState.first()
assertEquals(3, state.tasks.size)
}
}
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码