天天看点

Android内存优化14 内存泄漏常见情况5 特殊对象造成的内存泄漏 WebView内存泄漏

关于WebView的内存泄露,因为WebView在加载网页后会长期占用内存而不能被释放,因此我们在Activity销毁后要调用它的<code>destory()</code>方法来销毁它以释放内存。

另外在查阅<code>WebView</code>内存泄露相关资料时看到这种情况:

<code>Webview</code>下面的<code>Callback</code>持有<code>Activity</code>引用,造成<code>Webview</code>内存无法释放,即使是调用了<code>Webview.destory()</code>等方法都无法解决问题(Android5.1之后)。

前言:在项目的开发过程中,由于对内存要求较高,最近对应用的内存分析比较在意,前段时间监控图片内存,对Bitmap造成的内存泄漏进行了分析,并解决了问题。但是在图片内存泄漏之后,发现在访问网页的时候,webview竟然也会有内存泄漏,虽然内存占用很小,但是用户多次访问还是存在隐患。

于是,开始对webview进行内存分析,发现webview下面的callback持有activity引用,造成webview内存无法释放,在网上也找了很多方法,但是webview.destory()等方法大都无法解决问题。

最后看到一篇文章,才算明了出现这个bug的原因,按照作者的做法,确实解决了问题,安卓5.1和6.0系统都不存在内存泄漏问题。

文章附下:

销毁webview的方式

mWebView.removeAllViews();

/**、

* 这里内存泄漏了,因为它的父容器在退出前没有被销毁,所以就会持有引用,内存泄漏

* */

// mWebView.destroy();

改为

问题分析

这个问题还是比较严重的,那么进一步看详细的信息,找出到底是哪里引起的内存泄漏,详情的reference tree如下图所示:

从上图中可以看出,在第1层中的 TBReaderApplication 中的 mComponentCallbacks 成员变量,它是一个array list,它里面会持有住activity,引导关系是 mComponentCallbacks-&gt;AwContents-&gt;BaseWebView-&gt;BookDetailActivity, 代码在 Application 类里面,代码如下所示:

上面两个方法,会在 Context 基类中被调用,代码如下:

然后找到 org.chromium.android_webview.AwContents 类,看看这两个方法 onAttachedToWindow 和 onDetachedFromWindow:

系统会在attach处detach进行注册和反注册component callback,注意到 onDetachedFromWindow() 方法的第一行,if (isDestroyed()) return;, 如果 isDestroyed() 返回 true 的话,那么后续的逻辑就不能正常走到,所以就不会执行unregister的操作,通过看代码,可以得到,调用主动调用 destroy()方法,会导致 isDestroyed() 返回 true。

一般情况下,我们的activity退出的时候,都会主动调用 WebView.destroy() 方法,经过分析,destroy()的执行时间在onDetachedFromWindow之前,所以就会导致不能正常进行unregister()。

解决方案

找到了原因后,解决方案也比较简单,核心思路就是让onDetachedFromWindow先走,那么在主动调用之前destroy(),把webview从它的parent上面移除掉。

完整的代码如下:

Android 5.1之前的代码

对比了5.1之前的代码,它是不会存在这样的问题的,以下是kitkat的代码,它少了一行 if (isDestroyed()) return;,有点不明白,为什么google在高版本把这一行代码加上。

结束

在开发过程中,还发现一个支付宝SDK的内存问题,也是因为这个原因,具体的类是 com.alipay.sdk.app.H5PayActivity,我们没办法,也想了一个不是办法的办法,在每个activity destroy时,去主动把 H5PayActivity 中的webview从它的parent中移除,但这个问题限制太多,不是特别好,但的确也能解决问题,方案如下: