一、ANR 本质与原理
1. ANR 定义
当 Android 应用的主线程(UI线程)被阻塞超过一定时间时,系统会弹出"应用无响应"对话框,这就是 ANR(Application Not Responding)。
2. 关键阈值
场景 | 时间阈值 |
---|---|
按键或触摸事件响应 | 5秒 |
BroadcastReceiver 处理 | 前台10秒/后台60秒 |
Service 生命周期方法 | 前台20秒/后台200秒 |
ContentProvider 操作 | 10秒 |
3. 底层原理
-
Watchdog 机制:系统通过
ActivityManagerService
监控应用响应 -
消息队列检查:
Looper
通过Printer
检测消息处理时间 -
信号量机制:
SIGQUIT
信号触发堆栈收集
二、ANR 核心原因分析
1. 主线程阻塞
-
耗时操作:网络请求、数据库操作、文件IO
-
复杂计算:大数据处理、图像运算
-
同步锁竞争:死锁或长时间持有锁
2. 进程间通信
-
Binder 调用超时(特别是跨进程调用)
-
ContentProvider 响应慢
-
系统服务调用阻塞(如获取位置信息)
3. 内存问题
-
内存不足导致频繁GC
-
内存泄漏引发系统缓慢
-
过度内存分配触发系统限制
4. 系统资源竞争
-
CPU 资源被其他进程抢占
-
I/O 等待时间过长
-
系统服务响应延迟
三、ANR 诊断方法
1. 日志分析
adb pull /data/anr/traces.txt
分析关键信息:
-
主线程状态:查看
main
线程堆栈 -
锁等待:查找
blocked
或waiting
状态 -
CPU 使用:检查
CPU usage from XXX to YYY
2. Android Studio 工具
-
Profiler:CPU 和内存实时监控
-
Layout Inspector:检查 UI 层级复杂度
-
Database Inspector:分析数据库操作
3. 线上监控方案
// 使用 ANR-WatchDog 库检测
new ANRWatchDog().setANRListener(new ANRWatchDog.ANRListener() {
@Override
public void onAppNotResponding(ANRError error) {
// 上报 ANR 信息
reportANR(error.getStackTrace());
}
}).start();
四、ANR 规避策略
1. 主线程优化
// 将耗时操作移到工作线程
Executors.newSingleThreadExecutor().execute(() -> {
// 执行耗时操作
final Result result = doHeavyWork();
// 返回主线程更新UI
runOnUiThread(() -> updateUI(result));
});
2. 合理使用线程池
// 最佳实践线程池配置
private static final ThreadPoolExecutor NETWORK_POOL = new ThreadPoolExecutor(
3, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // 任务队列
new ThreadFactory() { // 线程工厂
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "NetworkThread #" + mCount.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
3. 异步加载优化
// 使用协程处理并发
lifecycleScope.launch {
val deferredData = async(Dispatchers.IO) { fetchData() }
val deferredImage = async(Dispatchers.IO) { loadImage() }
val data = deferredData.await()
val image = deferredImage.await()
updateUI(data, image) // 主线程自动切换
}
4. 广播接收优化
<!-- 在 Manifest 中设置超时时间 -->
<receiver
android:name=".MyReceiver"
android:directBootAware="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
// 使用 goAsync 延长处理时间
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final PendingResult result = goAsync();
new Thread(() -> {
// 执行耗时操作
doBackgroundWork();
// 结束广播处理
result.finish();
}).start();
}
}
五、ANR 解决方案
1. 锁优化技巧
// 避免锁嵌套
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void unsafeMethod() {
synchronized (lock1) {
synchronized (lock2) { // 容易导致死锁
// ...
}
}
}
// 改为安全方式
public void safeMethod() {
synchronized (lock1) {
// 操作 lock1 保护的数据
}
synchronized (lock2) {
// 操作 lock2 保护的数据
}
}
2. 数据库优化
// 使用事务批量操作
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();
try {
for (Data item : dataList) {
db.insert(TABLE_NAME, null, item.toContentValues());
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
3. 布局优化
<!-- 减少布局层级 -->
<ConstraintLayout>
<TextView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
app:layout_constraintTop_toBottomOf="@id/textView"
app:layout_constraintStart_toStartOf="parent"/>
</ConstraintLayout>
4. 内存优化
// 使用内存缓存
private LruCache<String, Bitmap> mMemoryCache;
// 初始化缓存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
六、高级调试技巧
1. StrictMode 检测
// 在 Application 中启用
public void onCreate() {
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.build());
}
}
2. 模拟 ANR 测试
// 在按钮点击中制造 ANR
findViewById(R.id.btn_test).setOnClickListener(v -> {
try {
Thread.sleep(10000); // 阻塞主线程 10 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
});
3. 性能分析工具
# 使用 systrace 分析
python systrace.py -a com.example.app -o trace.html sched freq idle am wm gfx view
七、架构级预防方案
-
任务调度中心:统一管理后台任务优先级
-
性能监控体系:实时监控主线程健康度
-
异步编程规范:强制使用协程/RxJava
-
自动化测试:Monkey + ANR 专项测试
通过以上系统化的方法,可以显著降低 ANR 发生率。结合实际案例(如解决某个具体 ANR 问题的过程)能更好展示你的技术深度。