当前位置: 移动技术网 > 科技>办公>内存 > webview内存泄漏解决方案

webview内存泄漏解决方案

2020年09月29日  | 移动技术网科技  | 我要评论
1.运行app,先用AS自带的Profiler分析我们的WebViewActivity,频繁进出,看内存占用情况,会发现内存在不断的上升,而且退出当前页面内存只是下降一点,一直持续下去,肯定会OOM;2.引入leakcanary内存泄漏分析工具由于只运行debug模式来检测,所以只需在build.gradle中引入:debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'然后在自定义的Application的O

1.运行app,先用AS自带的Profiler分析我们的WebViewActivity,频繁进出,看内存占用情况,会发现内存在不断的上升,而且退出当前页面内存只是下降一点,一直持续下去,肯定会OOM;
2.引入leakcanary内存泄漏分析工具
由于只运行debug模式来检测,所以只需在build.gradle中引入:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'

然后在自定义的Application的OnCreate方法中加入如下:

if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
   }
LeakCanary.install(this);

接下来重新编译运行APP,继续频繁进出WebviewActivity,LeakCanary会很快检测出内存泄漏风险,等待通知栏下载hprof文件成功,然后点击Android Studio 页面右下角'Device File Explorer',会显示当前手机的文件夹,
 找到"sdcard--->Download--->leakcanary-com.xxx.xxx(后缀是自己包名的文件夹)--->2020-09-29_10-06-28_940.hprof",找到这个文件保存在电脑本地,然后使用工具分析,这里分析的的方式有很多种:
 1.直接使用AS打开,会自动fetch然后可以查看各变量类型使用情况;
 2.使用Eclipse的Eclipse Memory Analyzer;
 3.使用在线分析工具:https://heaphero.io/heap-index.jsp#header(个人比较喜欢使用这个);
 接下来直接按照第三个方式来举例了:
 这是分析的整体结果图:

然后查看Large objects,会发现webview和drawable占用内存较高,一步步点进去 会发现是Bitmap的buffer导致的内存泄漏风险

 

那么再回想一下WebViewActivity的布局原来有一张无网的背景图在WebViewActivity频繁进出时没有及时回收导致的(Android5.1和Android8.0检测结果还有差异)。那就开始优化之路--及时回收ImageView组件和Bitmap: 

// ivNoNetwork是无网的ImageView组件
	if(ivNoNetwork!= null ){
		Drawable drawable = ivNoNetwork.getDrawable();
		if (drawable != null && drawable instanceof BitmapDrawable) {
			BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
			Bitmap bitmap = bitmapDrawable.getBitmap();
			if (bitmap != null && !bitmap.isRecycled()) { // 如果还没被回收,才去回收
				bitmap.recycle();
			}
		}
	}
	
	// 同时在父布局中移除这个ImageView, llNoNetwork是ImageView组件的父布局 
	if(llNoNetwork != null){
	  llNoNetwork.removeView(ivNoNetwork);  //必须是直接remove它,如果使用removeAllView方法,当很快速度频繁进出时,效率太慢  还是会报leak
	}
	ivNoNetwork = null;

然后重新运行再检测,就不会再报内存泄漏风险了。

再说一下Webview的常规优化方案:
1.使用动态添加Webview的方式

// 动态添加Webview
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT);
        WeakReference weakReference = new WeakReference(this); //使用弱引用去持有当前Activity
        X5WebView webView = new X5WebView((Context) weakReference.get(),null);
        webView.setLayoutParams(params);
        llwebLayout.addView(webView);	

2.注册的服务、监听器、handler要及时移除并置为null

3.如果当前页面有ImageView,也要及时回收、销毁

4.最后再执行super.onDestroy()方法  

@Override
    protected void onDestroy() {
        try{
            if(mMyRevicer != null){
                unregisterReceiver(mMyRevicer);
                mMyRevicer = null;
            }

            if(llwebLayout != null){
                llwebLayout.removeAllViews();
            }
			
			// webView清除缓存并销毁
            if (webView != null) {
                webView.stopLoading();
                webView.clearHistory();
                webView.removeAllViewsInLayout();
                webView.removeAllViews();
                webView.setWebViewClient(null);
                webView.destroy();
                webView = null;
            }

			// 那张无网背景大图,主动回收
            if (ivNoNetwork != null) {
                Drawable drawable = ivNoNetwork.getDrawable();
                if (drawable != null && drawable instanceof BitmapDrawable) {
                    BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
                    Bitmap bitmap = bitmapDrawable.getBitmap();
                    if (bitmap != null && !bitmap.isRecycled()) {
                        bitmap.recycle();
                    }
                }
            }
			
            if (llNoNetwork != null) {
                llNoNetwork.removeView(ivNoNetwork);   //ImageView的父组件必须是直接remove当前ImageView
            }
            ivNoNetwork = null;
            llNoNetwork = null;
            llwebLayout = null;
            mClickListener= null;
        }catch (Throwable throwable){
            throwable.printStackTrace();
        } finally {
            super.onDestroy();
        }
    }

如果你使用的是腾讯的X5Webview,在Activity的onDestory方法中最好不要使用这个方法,源码中可以查看到它会new一个新的webview,虽然方法结尾会置空为null

  //官方文档介绍这个API是:一次性删除所有缓存
 // QbSdk.clearAllWebViewCache(this,true);  

这个是X5Webview SDK的源码:(基于implementation 'com.tencent.tbs.tbssdk:sdk:43939')

public static void clearAllWebViewCache(Context var0, boolean var1) {
        TbsLog.i("QbSdk", "clearAllWebViewCache(" + var0 + ", " + var1 + ")");
        boolean var2 = false;

        WebView var3;
        try {
            var3 = new WebView(var0);
            if (var3.getWebViewClientExtension() != null) {
                var2 = true;
                u var4 = com.tencent.smtt.sdk.u.a();
                if (null != var4 && var4.b()) {
                    var4.c().a(var0, var1);
                }
            }

            var3 = null;
        } catch (Throwable var6) {
            TbsLog.e("QbSdk", "clearAllWebViewCache exception 2 -- " + Log.getStackTraceString(var6));
        }

        ...
    }

        以上即是本人解决Webview 内存泄漏的一些个人心得,希望对你有所帮助~

本文地址:https://blog.csdn.net/qq_33539839/article/details/108866646

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网