本篇文章,主要记录关于使用Profile进行Android多线程问题的辅助定位和解决实战。
- 大纲
- 问题现象与背景
- 问题初步分析与假设
- 问题定位过程(核心)
- 根因总结
问题现象与背景
在业务 App “Note” 中,我们有一个通过手势“画圈”快速选中元素的功能,用户无需切换工具即可圈选目标元素。但在实际使用中,偶现圈选后目标元素从界面中消失,但选中框仍然正常显示的问题。

业务实现背景:
为了区分普通元素绘制与选中状态下的高亮绘制,我们使用了两个 Bitmap 进行绘制:
offscreenBitmap:用于绘制所有正常显示的元素;
selectedBitmap:用于绘制当前被选中的元素高亮区域;
整体绘制与选中流程如下:
- 子线程 将目标元素绘制到
selectedBitmap上(用于 UI 中展示选中框内高亮);
- 主线程 将对应元素从
offscreenBitmap中擦除(避免重复显示);
- 最后,将选中框 UI 叠加渲染到界面上;
为保证线程安全,上述操作使用了锁机制,确保子线程不会在主线程完成绘制前操作
offscreenBitmap。结合业务流程与现象(元素消失但选中框存在),初步推测该问题为多线程同步问题导致的数据不一致。
问题定位过程
Profile的Trace抓取功能,能清晰地记录当前每个线程的执行情况,对于多线程问题的定位非常有帮助,可以从调用时序中,清晰看到方法的执行顺序。
为定位问题,使用 Android Studio 提供的 CPU Profiler 工具,通过抓取 Trace 文件,对比 正常情况下与异常情况下的线程执行顺序,寻找差异点。
正常情况 Trace 分析:
- 主线程按序执行:先绘制目标元素到
offscreenBitmap,然后调用drawSelectedArea()方法;
- 子线程由于持有锁,在主线程完成 offscreenBitmap 绘制之后,才开始执行,将对应内容绘制到
selectedBitmap;
- 整体流程有序,子线程读取到的
offscreenBitmap数据是正确的;
注:图片中,上面的为主线程,下面的为子线程


异常情况 Trace 分析:
在多次尝试复现问题后,抓取到了一次典型异常 Trace,发现如下关键差异:
- 在子线程等待锁期间,主线程提前调用了
eraserSelectedArea()方法,将刚刚绘制到 offscreenBitmap 上的目标元素区域进行了擦除;
- 之后,子线程才获取到锁,开始执行,从
offscreenBitmap中读取对应区域数据进行绘制;
- 但由于该区域已经被擦除,子线程实际上读取到的是“空白区域”,最终导致绘制到
selectedBitmap的内容为空;
- 后续渲染时,选中区域看似选中但实际上没有内容,表现为“元素消失”。

结合这个错误的流程,我们可以推测问题的发生步骤:
- 画圈选择时,主线程先绘制选中的元素到offscreenBitmap(这里还是正常的)
- 此时按照计划,应该要轮到子线程把主线程绘制的内容,绘制到selectedBitmap中,但是,中间却来了个不速之客,eraserSelectedArea将刚刚主线程才绘制的内容,给清空了。
- 这时候子线程终于拿到锁了,开始执行,浑然不知offscreenBitmap的对应区域已经被清空了,还傻傻从offscreenBitmap中绘制内容到selectedBitmap(但是该区域已经被清空了,当然拿不到内容),此时就会拿到空的内容,导致看起来元素丢失了。
- 由于子线程并不知道出错了,所以会继续进入后续流程,进而显示选中框。
根因总结
经过对比分析,问题的根本原因在于多线程时序控制不当:
虽然使用了锁来保证线程互斥访问
offscreenBitmap,但关键方法 eraserSelectedArea()的调用时机未被正确约束,导致其在子线程读取数据前,错误地清空了 offscreenBitmap 上的目标区域,造成子线程读取到了无效数据,最终渲染异常。简而言之:关键操作(擦除)未与绘制/读取操作严格同步,导致数据竞争与状态不一致。
最终解决
既然已经知道是时序问题,导致主线程错误的把选择区域的内容擦除了,这里可以在画圈选择进入选中状态前,直接把主线程的擦除任务移除,避免对应区域被错误擦除。将绘制的逻辑,统一交给画圈选择的流程。
- Author:CoderWdd
- URL:https://www.wuinsights.top//article/8be9832d-46ae-471d-a604-d68b94db7535
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts