Why WebView and Dialogs Cause Android Memory Leaks on Pre‑Lollipop Devices
This article investigates a surge of memory‑leak bugs in an Android app, tracing the root causes to WebView usage, CookieSyncManager, Dialog listeners, and handler threads on Android 4.3 devices, and offers concrete mitigation steps for developers.
一. 内存泄漏的 Bug 猛增
During monkey testing of the app, several memory‑leak bugs were detected, with four bug reports received at once, causing great concern.
Login page shows memory leaks despite seemingly perfect code.
What is an Activity leak? In Android an Activity represents a UI page with a lifecycle; after the lifecycle ends the VM should reclaim its memory. A leak means the Activity remains referenced and is not reclaimed.
Usually Activity leaks are caused by Handler inner classes staying on a thread, but that has already been handled in the app, so the source of the leak is investigated further.
二. WebView 导致内存泄漏众所周知
Three different reference chains were examined:
① AuthDialog reference chain
② BrowserFrame reference chain
③ IClipboradDataPaste reference chain
All bugs originated from a Samsung GT‑I9300 running Android 4.3. Investigation revealed three classes frequently causing leaks: CookieSyncManager, WebView, and WebViewClassic.
CookieSyncManager is a global static singleton that uses an Activity as Context. It should be created early with applicationContext to avoid holding an Activity.
When a WebView‑using Activity is destroyed, call WebView.onPause() and WebView.destroy() to release resources.
WebView leaks are well‑known; it is recommended to run WebView in a separate process and kill that process after use.
However, the main process LoginActivity does not use WebView at all.
三. 第三方 jar 包使用 WebView 这可如何是好
The AuthDialog reference chain points to a third‑party SDK that creates a WebView with the host Activity as Context.
Using an Activity as the WebView Context can easily cause leaks. The SDK does call WebView.destroy(), but there is no guarantee that dismiss() will always be executed.
Because the SDK is a third‑party library, moving WebView to another process is not feasible, so mitigation must be done in the app.
Conclusion: on Android 4.3 and below, creating WebView with an Activity can lead to leaks.
四. 心结未解,翻看 WebView 源码了解根源
Examining Android 4.3 source reveals the WebView architecture:
WebViewFactory creates a global singleton WebViewClassic$Factory.
WebViewClassic implements the WebView API and maintains a WebViewCore.
WebViewCore spawns a permanent thread "WebViewCoreThread" which creates and invokes BrowserFrame.
BrowserFrame is a Handler subclass running on that thread and is called from native code.
BrowserFrame also invokes CookieSyncManager.createInstance().
CookieSyncManager itself runs a global singleton thread that syncs cookies to flash storage every five minutes.
The key objects that may retain the Activity reference are WebViewClassic, WebViewCore, and BrowserFrame.
Although WebViewClassic and WebViewCore share the WebView lifecycle, a lingering BrowserFrame handler can keep a reference chain: Thread → MessageQueue → Message → Handler (BrowserFrame) → Activity.
五. 最后的疑惑
Further analysis of the AuthDialog reference chain shows that CookieSyncManager's thread directly references a Message object whose obj field holds the AuthDialog$3 (OnDismissListener) instance.
MAT analysis confirms the chain: Thread(main) → MessageQueue → Message → obj(OnDismissListener) → AuthDialog → Activity.
However, this chain seems unrelated to the CookieSyncManager thread, leading to confusion about how the Message from the main thread ends up in the CookieSyncManager thread.
Investigation shows that the CookieSyncManager thread obtains a Message from the global pool (via obtainMessage()) that still holds the OnDismissListener reference, and because Dialog keeps a copy of this Message as mDismissMessage, the Message never returns to the pool, causing a leak.
The final reference chain is: Thread(CookieSyncManager) → Message → AuthDialog$3(OnDismissListener) → AuthDialog → Activity.
六. 原来是它!—Dialog
According to a GitHub article "A Bloodshed Caused by a Memory Leak", using AlertDialog before Android Lollipop can cause leaks because local variables in Dalvik VM may remain referenced if a thread loops without clearing them.
When a thread repeatedly obtains a Message from the pool and assigns it to a local variable, the VM does not nullify the reference after the loop, so the Message (and its obj) stays alive.
In the case of Dialog, the mDismissMessage holds the OnDismissListener, preventing the Message from being recycled, and a background CookieSyncManager thread keeps a reference to that Message, forming a leak.
七. 总结一些注意点
For Android 4.3 and earlier (or Dalvik‑based versions):
When using WebView, ensure destroy() is called.
Prefer creating WebView with applicationContext() instead of an Activity.
When setting OnShowListener, OnDismissListener, or OnCancelListener on Dialogs, watch out for inner classes that may leak the Activity.
Avoid holding Message objects yourself.
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.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
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.
