Skip to content

Android 开发

面向有一定编程基础、想系统掌握 Android 开发的笔记。主线是 Kotlin 语言 + 四大组件与生命周期 + Jetpack Compose(或 XML)+ 官方架构(ViewModel/Flow/Hilt),副线是打包、多渠道与上架。示例基于 Kotlin 2.x 与 Android 平台当前稳定线(AGP 8.x / Compose BOM 2024+)语境编写,需在 Android Studio 中验证。

Kotlin 语言要点

  • 空安全:类型系统区分可空与非空(String? vs String),调用可空值要 ?.?:!!(尽量不用 !!);
  • 数据类data class 自动生成 equals/hashCode/toString/copy,适合做模型与状态;
  • 扩展函数:给类加方法而不改类,Jetpack/协程大量用它;
  • 协程与 Flowsuspend 函数是异步主线;launch 起协程、async/await 并发、Flow 做响应式数据流(.map/.collect/.flowOn);作用域与结构化并发的纪律比语法更重要;
  • 委托与伴生by lazyobject 单例、companion object 静态成员。

一个 ViewModel + Flow 示例:

kotlin
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 Catalog libs.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 桥接,不必一刀切重写。
kotlin
@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

kotlin
@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)

kotlin
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 引用内存泄漏、旋转后更新已死 UIViewModel 只放状态与数据源,不持 View
协程作用域错绑Activity 销毁后协程还在跑/报错viewModelScope/lifecycleScope + repeatOnLifecycle
主线程做 IOANR(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 包在真机冒烟过一遍(网络/权限/更新)。

相关链接:技术全景与选型 · 网络层与本地存储 · 打包、签名与发布 · 性能与稳定性 · 写作规范见客户端开发索引

基于 VitePress 构建 · 内容以知识共享方式沉淀