IO 性能优化
问题
Android 应用中的 IO 操作有哪些性能隐患?如何优化?
答案
主线程 IO 的危害
主线程进行磁盘 IO 是 ANR 的常见原因之一。即使单次 IO 很快,在磁盘繁忙或存储空间不足时也可能耗时数百毫秒。
StrictMode 检测
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.penaltyLog()
.build()
)
}
// 日志输出示例:
// D/StrictMode: StrictMode policy violation:
// android.os.StrictMode$StrictModeDiskReadViolation
常见 IO 优化
| 场景 | 问题 | 优化 |
|---|---|---|
| SharedPreferences | getString() 首次加载阻塞主线程 | 改用 DataStore |
| 文件读写 | 主线程同步读写 | withContext(Dispatchers.IO) |
| 数据库查询 | 主线程 query | Room + suspend / Flow |
| Bitmap 解码 | BitmapFactory.decodeFile 阻塞 | Coil/Glide 异步加载 |
| JSON 解析 | 大文件一次性解析 | 流式解析(JsonReader) |
批量写入优化
// ❌ 逐条插入
for (item in items) {
db.itemDao().insert(item) // 每次一个事务
}
// ✅ 批量插入(单事务)
db.withTransaction {
db.itemDao().insertAll(items)
}
MMKV 替代 SP
// MMKV 基于 mmap,性能远超 SharedPreferences
val mmkv = MMKV.defaultMMKV()
mmkv.encode("key", "value")
val value = mmkv.decodeString("key")
| 特性 | SharedPreferences | DataStore | MMKV |
|---|---|---|---|
| 写入速度 | 慢(XML) | 中等 | 极快(mmap) |
| 线程安全 | apply() 可能丢失 | ✅ | ✅ |
| 多进程 | ❌ | ❌ | ✅ |
| 类型安全 | ❌ | ✅ | ❌ |
常见面试问题
Q1: SP 的 apply() 为什么可能导致 ANR?
答案:
apply() 虽然异步写入磁盘,但 Activity 在 onPause() → onStop() 时会通过 QueuedWork.waitToFinish() 等待所有 SP 的异步写入完成。如果写入量大或磁盘慢,主线程会被阻塞导致 ANR。
Q2: 大文件 JSON 如何高效解析?
答案:
使用流式解析(Streaming Parser)而非一次性加载到内存:
// Gson 流式解析
val reader = JsonReader(FileReader(file))
reader.beginArray()
while (reader.hasNext()) {
val item = gson.fromJson<Item>(reader, Item::class.java)
processItem(item)
}
reader.endArray()