Analysis and Fix of Android Memory Leak Caused by AsyncTask SerialExecutor Blocking on Vivo Devices
The analysis reveals that on Vivo devices running Android 9, a static AsyncTask SERIAL_EXECUTOR blocks the UpdateBottomFlagTask in several order‑related activities, leaking the Activity context, and the fix replaces this executor via reflection with a custom parallel SerialExecutorProxy to prevent the leak.
背景:APM平台显示得物Android交易线全部版本内存泄漏率在11.9号从2.13%涨到17.18%。
问题分析:通过内存泄漏分析发现泄漏集中在OrderConfirmActivityV2、OrderDetailActivityV2、BuyerShippingDetailActivity三个页面。进一步定位到AsyncTask的静态变量SERIAL_EXECUTOR导致任务队列阻塞,UpdateBottomFlagTask引用Activity Context无法释放。
引用链信息:AsyncTask.SERIAL_EXECUTOR → SerialExecutor.mTasks → ArrayDeque.elements → UpdateBottomFlagTask → AbsListView → BuyerShippingDetailActivity Context。
根源:Vivo 9.0 ROM在AbsListView中新增内部类UpdateBottomFlagTask(继承AsyncTask),在doInBackground中判断isSuperFloatViewServiceRunning()(判断是否存在截长屏服务SuperShotFloatViewService),若存在则更新系统Setting。由于AsyncTask的串行执行机制,前序耗时任务阻塞队列导致UpdateBottomFlagTask得不到及时执行,造成Context泄漏。
解决方案:通过反射替换AsyncTask的SERIAL_EXECUTOR为自定义的SerialExecutorProxy,使每个任务分配新线程执行(或仅对UpdateBottomFlagTask单独处理),避免队列阻塞。代码示例:
class VivoLeakFixUtil { public static void initVivoLeakHander() { if ((Build.VERSION.SDK_INT == Build.VERSION_CODES.P) && isVivo()) { try { setFinalStatic(AsyncTask.class.getDeclaredField("SERIAL_EXECUTOR"), new SerialExecutorProxy()); Field defaultfield = AsyncTask.class.getDeclaredField("sDefaultExecutor"); defaultfield.setAccessible(true); defaultfield.set(null, AsyncTask.SERIAL_EXECUTOR); Timber.tag(TAG).d("initVivoLeakHander success"); } catch (Exception e) { Timber.tag(TAG).e(e); } } } private static boolean isVivo() { return Build.MANUFACTURER.contains("vivo") || Build.MANUFACTURER.contains("VIVO"); } private static void setFinalStatic(Field field, Object newValue) throws Exception { field.setAccessible(true); Field accessFlagsFiled = Field.class.getDeclaredField("accessFlags"); accessFlagsFiled.setAccessible(true); accessFlagsFiled.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); }}class SerialExecutorProxy implements Executor { private static final String TAG = "ActivityLRT"; private final AtomicInteger threadNumber = new AtomicInteger(1); @Override public synchronized void execute(Runnable r) { String name = "thread---" + threadNumber.getAndIncrement(); Timber.tag(TAG).d("scheduleNext thread name = %s", name); new Thread(r, name).start(); }}在Application初始化时根据配置中心开关调用VivoLeakFixUtil.initVivoLeakHander();
本地验证:在Demo中启动多个耗时AsyncTask阻塞队列,进行截长屏操作触发UpdateBottomFlagTask,未采用 fix 时发生内存泄漏;采用 fix 后任务并行执行,泄漏消失。
后续计划:通过埋点监控AsyncTask执行信息(线程状态、等待时间、开始/结束时间、执行时长),定位阻塞任务并进行优化。
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
DeWu Technology
A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
