问题背景
在排查项目内存泄漏过程中发现了一些由WebView引起的内存泄漏,经过测试发现该部分泄漏只会出现在android 5.1及以上的机型。虽然项目使用WebView的场景并不多,但秉承着一个泄漏都不放过的精神,我们肯定要把它给解决了。
遇到的问题
项目中使用WebView的页面主要在FAQ页面,问题也出现在多次进入退出时,发现内存占用大,GC频繁。使用LeakCanary观察发现有两个内存泄漏很频繁:
我们分析一下这两个泄漏:
从图一我们可以发现是WebView的ContentViewCore中的成员变量mContainerView引用着AccessibilityManager的mAccessibilityStateChangeListeners导致activity不能被回收造成了泄漏。
引用关系:mAccessibilityStateChangeListeners->ContentViewCore->WebView->SettingHelpActivity
从图二可以发现引用关系是: mComponentCallbacks->AwContents->WebView->SettingHelpActivity
问题分析
我们找找mAccessibilityStateChangeListeners 与 mComponentCallbacks是在什么时候注册的,我们先看看mAccessibilityStateChangeListeners
AccessibilityManager.java
private final CopyOnWriteArrayList
mAccessibilityStateChangeListeners &#61; new CopyOnWriteArrayList<>();
/**
* Registers an {&#64;link AccessibilityStateChangeListener} for changes in
* the global accessibility state of the system.
*
* &#64;param listener The listener.
* &#64;return True if successfully registered.
*/
public boolean addAccessibilityStateChangeListener(
&#64;NonNull AccessibilityStateChangeListener listener) {
// Final CopyOnWriteArrayList - no lock needed.
return mAccessibilityStateChangeListeners.add(listener);
}
/**
* Unregisters an {&#64;link AccessibilityStateChangeListener}.
*
* &#64;param listener The listener.
* &#64;return True if successfully unregistered.
*/
public boolean removeAccessibilityStateChangeListener(
&#64;NonNull AccessibilityStateChangeListener listener) {
// Final CopyOnWriteArrayList - no lock needed.
return mAccessibilityStateChangeListeners.remove(listener);
}
上面这几个方法是在AccessibilityManager.class中定义的&#xff0c;根据方法调用可以发现在ViewRootImpl初始化会调用addAccessibilityStateChangeListener 添加一个listener&#xff0c;然后会在dispatchDetachedFromWindow的时候remove这个listener。
既然是有remove的&#xff0c;那为什么会一直引用着呢&#xff1f;我们稍后再分析。
我们再看看mComponentCallbacks是在什么时候注册的