显示全屏进度时如何避免ANR问题?
解决全屏同步进度界面的ANR问题
核心原因分析
从你提供的日志来看,ANR触发的本质是主线程被SQLite数据库操作(sqlite3_exec/sqlite3_step)阻塞,系统无法及时处理用户的触摸输入事件,最终触发Input dispatching timed out。即使进度动画在更新,只要主线程被耗时的数据库同步操作占用,就会导致输入事件处理超时。
具体解决方案
1. 强制将所有数据库同步操作移至后台线程
必须保证所有EntityFramework+SQLite的同步逻辑(查询、插入、更新)完全在后台线程执行,主线程仅负责接收进度回调并更新UI(动画、进度文本)。绝对禁止在主线程调用任何SQLite相关API,包括EF上下文的读写操作。
- 可选用的实现方式:
ThreadPoolExecutor、Coroutine(Kotlin)、AsyncTask(注意适配高版本Android)
2. 拦截全屏进度界面的触摸事件
既然同步期间不允许用户操作,直接在全屏进度的根容器(如Dialog/Activity的根View)上拦截所有触摸事件,避免事件进入系统输入分发队列:
- XML布局方式:给根View添加以下属性
android:clickable="true" android:focusable="true" android:focusableInTouchMode="true" - 代码方式:重写View的触摸事件方法
@Override public boolean onTouchEvent(MotionEvent event) { // 消费所有触摸事件,不传递也不做额外处理 return true; }
3. 优化旧版SQLite EF的操作效率
由于无法升级SQLite版本,针对2.2.6版本做针对性优化:
- 用批量操作替代单条数据循环处理,减少
sqlite3_step调用次数 - 用事务包裹全部同步操作,避免频繁磁盘IO:
using (var transaction = dbContext.Database.BeginTransaction()) { // 执行所有同步插入/更新逻辑 dbContext.SaveChanges(); transaction.Commit(); } - 提前预加载同步所需数据,减少同步过程中的查询操作
4. 系统级触摸屏蔽(备选方案)
如果上述拦截方式仍无效,给当前窗口添加触摸屏蔽Flag:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
同步完成后记得清除Flag:
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
验证方式
同步期间用adb shell dumpsys activity activities检查主线程状态,确认无数据库操作在主线程执行;多次点击进度界面,观察是否再触发ANR日志。
内容的提问来源于stack exchange,提问作者krs
相关产品推荐
相关产品推荐

