欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

webview内存泄漏解决方案

程序员文章站 2022-06-23 14:15:12
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(个人比较喜欢使用这个);
 接下来直接按照第三个方式来举例了:
 这是分析的整体结果图:

webview内存泄漏解决方案

webview内存泄漏解决方案

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

 webview内存泄漏解决方案

那么再回想一下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