Android 开发
面向有一定编程基础、想系统掌握 Android 开发的笔记。主线是 Kotlin 语言 + 四大组件与生命周期 + Jetpack Compose(或 XML)+ 官方架构(ViewModel/Flow/Hilt),副线是打包、多渠道与上架。示例基于 Kotlin 2.x 与 Android 平台当前稳定线(AGP 8.x / Compose BOM 2024+)语境编写,需在 Android Studio 中验证。
Kotlin 语言要点
- 空安全:类型系统区分可空与非空(
String?vsString),调用可空值要?.、?:、!!(尽量不用!!); - 数据类:
data class自动生成 equals/hashCode/toString/copy,适合做模型与状态; - 扩展函数:给类加方法而不改类,Jetpack/协程大量用它;
- 协程与 Flow:
suspend函数是异步主线;launch起协程、async/await并发、Flow做响应式数据流(.map/.collect/.flowOn);作用域与结构化并发的纪律比语法更重要; - 委托与伴生:
by lazy、object单例、companion object静态成员。
一个 ViewModel + Flow 示例:
data class User(val id: Int, val name: String)
class UserViewModel(private val repo: UserRepository) : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users.asStateFlow()
fun load() {
viewModelScope.launch {
runCatching { repo.fetchUsers() }
.onSuccess { _users.value = it }
.onFailure { /* 统一错误提示 */ }
}
}
}纪律:ViewModel 不持有 Activity/View 引用(旋转后 View 重建,ViewModel 仍存);收集 Flow 要用 repeatOnLifecycle 或 UI 层 collectAsStateWithLifecycle。
工程结构与清单文件
- Gradle/AGP:模块
build.gradle.kts声明依赖与编译配置;用 Gradle 版本目录(Version Cataloglibs.versions.toml)管理版本; - AndroidManifest.xml:声明组件、权限、启动 Activity、主题。四大组件都必须在这里注册,漏声明运行即崩;
- Application 类:进程启动最先执行,适合初始化 SDK,但要克制——初始化越多冷启动越慢(见性能与稳定性);
- 模块化:业务按功能模块拆 Gradle module,公共层抽 lib 模块。
四大组件与生命周期
| 组件 | 职责 | 生命周期要点 |
|---|---|---|
| Activity | 一屏交互 | onCreate/onStart/onResume/onPause/onStop/onDestroy;配置变更(旋转)会销毁重建 |
| Service | 后台任务 | started/bound 两种;前台服务需通知栏常驻(Android 14+ 需声明前台服务类型) |
| BroadcastReceiver | 系统/应用事件 | 动态注册记得注销;onReceive 限时执行 |
| ContentProvider | 跨应用数据共享 | 一般由框架初始化驱动 |
关键认知:
- 进程可能随时被系统回收:被回收前只会回调 onSaveInstanceState/ViewModel.onCleared,任何「不保存会丢」的数据必须在变更时持久化;
- 配置变更不是异常是常态:旋转、深色模式、分屏都会重建 Activity。状态放 ViewModel(幸存) + 重建时恢复(savedStateHandle)双保险;
- 后台任务优先 WorkManager:它保证在满足条件时执行、进程重启后继续、可串链,优于裸 Service。
Jetpack Compose:声明式 UI
Compose 把「UI = 函数(状态)」落到 Android:
@Composable函数描述 UI;状态变化触发重组(recomposition),只更新变化部分;- 状态用
remember { mutableStateOf(...) }(本地)与StateFlow/ViewModel(跨重组); - 布局用
Column/Row/Box+ 修饰符链(Modifier.padding().fillMaxWidth().clickable{}); - 主题走 Material 3
MaterialTheme,动态取色dynamicColor = true仅在 Android 12+ 可用; - 与 XML 混编:老项目可用
AndroidView/ComposeView桥接,不必一刀切重写。
@Composable
fun UserRow(user: User) {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp)
) {
Text(user.name, style = MaterialTheme.typography.titleMedium)
}
}重组纪律:大列表用 LazyColumn(懒加载);derivedStateOf 把「由状态推得的值」收窄;不要在每个 item 组合里做耗时计算。
导航
官方 androidx.navigation + Compose:
- 导航图定义目的地(Composable 路由),
NavController跳转; - 传参走序列化(
@Serializable路由模型),少传大对象,传 id 再按需加载; - 深链(Deep Link)用显式声明 route,方便通知/外链直达页面。
架构:MVVM + 官方组件栈
单 Activity + Compose 时代的推荐分层:
| 层 | 组件 | 职责 |
|---|---|---|
| UI 层 | Composable + ViewModel | 渲染状态、收集事件、不碰数据细节 |
| 领域层(可选) | UseCase | 业务用例编排 |
| 数据层 | Repository | 屏蔽数据来源:网络/本地/缓存 |
| 数据源 | Retrofit/OkHttp、Room、DataStore | 具体读写 |
依赖注入用 Hilt:@HiltViewModel + @Inject constructor 把 Repository 注入 ViewModel,Application 打 @HiltAndroidApp。
@HiltViewModel
class FeedViewModel @Inject constructor(
private val feedRepo: FeedRepository
) : ViewModel() { /* ... */ }本地存储
| 场景 | 方案 |
|---|---|
| 键值偏好 | Jetpack DataStore(Preferences)——替代 SharedPreferences,支持 Flow 与协程 |
| 结构化数据 | Room(SQLite 之上,编译期校验 SQL 与实体映射,支持 Flow 响应查询) |
| 缓存/文件 | 应用内部存储(filesDir/cacheDir),图片缓存见性能与稳定性 |
| 敏感凭证 | Android Keystore + EncryptedSharedPreferences/加密数据库 |
| 后台任务 | WorkManager(一次性/周期/约束触发) |
网络
标准组合 Retrofit + OkHttp + kotlinx.serialization(或 Moshi/Gson):
interface Api {
@GET("users")
suspend fun users(): List<User>
}
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.addInterceptor(HeaderInterceptor()) // 统一 token、UA
.addInterceptor(HttpLoggingInterceptor()) // debug 才开日志
.build()
val api = Retrofit.Builder()
.baseUrl(BuildConfig.API_BASE_URL)
.client(client)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
.create(Api::class.java)工程要点(详见网络层与本地存储):统一 BaseUrl 按构建变体切换、错误体结构化、缓存策略与弱网提示、请求取消与生命周期绑定、token 过期刷新。
权限与多设备
- 运行时权限:危险权限(相机/定位/通知)要动态申请,Android 6+ 机制 + 说明文案引导;
- targetSdk 跟随政策:商店对 targetSdk 有最低要求,别停在旧版本吃「存储分区/后台限制」的亏;
- 多设备适配:用资源限定符(
values-w600dp等)适配平板;自适应 icon 输出多尺寸;深色模式用主题资源而非硬编码颜色。
发布路线
调试完成 → ./gradlew bundleRelease(出 AAB)→ 上传 Google Play(签名用 Play App Signing)或国内多渠道(出 APK 需各渠道包)→ 灰度与全量。签名、版本号与 CI 全流程见打包、签名与发布。
常见坑速查
| 坑 | 现象 | 对策 |
|---|---|---|
| 漏注册组件 | 跳转/接收器直接崩 ClassNotFoundException | 新组件必查 Manifest |
| ViewModel 持 Activity 引用 | 内存泄漏、旋转后更新已死 UI | ViewModel 只放状态与数据源,不持 View |
| 协程作用域错绑 | Activity 销毁后协程还在跑/报错 | viewModelScope/lifecycleScope + repeatOnLifecycle |
| 主线程做 IO | ANR(Application Not Responding) | 网络/磁盘一律走协程 IO 或 WorkManager |
| 混淆不配置规则 | 发布版反序列化/反射崩 | R8 规则文件补 serialization/反射类 keep(见 release 页) |
| 明文 HTTP 被拦 | debug 正常 release 连不上 | 配 networkSecurityConfig 白名单,别全局明文 |
| 大图直接加载 | 内存暴涨 OOM | 采样解码(BitmapFactory inSampleSize)或 Glide/Coil |
| DataStore 在协程外读写 | 卡主线程/抛异常 | DataStore 的 IO 走协程,避免阻塞 UI |
| 忘适配深色/大字体 | 上线被吐槽、商店截图不符 | 资源主题化 + 动态字体遍历一遍 |
| 国际化硬编码字符串 | 多语言上架文案缺失 | 所有文案走 strings.xml 资源 |
| 权限被拒没有引导 | 功能不可用且用户不知怎么恢复 | 申请前说明用途,被拒后跳设置引导 |
| 后台服务被系统杀 | 「推送/上传怎么没了」 | 前台服务 + WorkManager 方案,别对抗系统 |
检查清单
- [ ] 四大组件注册齐全,Manifest 无冗余权限;
- [ ] 状态放 ViewModel,配置变更不丢关键状态;
- [ ] 网络统一走 Retrofit + 拦截器,错误与超时可重试;
- [ ] Room/DataStore 走协程,敏感数据加密或入 Keystore;
- [ ] 发布构建过 R8 混淆,序列化/反射类已配置 keep 规则;
- [ ] 深色模式、动态字体、平板布局遍历通过;
- [ ] release 包在真机冒烟过一遍(网络/权限/更新)。