Android WebView常见的坑和解决方案

14,517 阅读4分钟

一、WebView常见的一些坑

1、内存泄漏

由于WebView是依附于Activity的,Activity的生命周期和WebView启动的线程的生命周期是不一致的,这会导致WebView一直持有对这个Activity的引用而无法释放,解决方案如下三种:

解决方案:

(1)独立进程,简单暴力,不过可能涉及到进程间通信。使webview产生了oom崩溃等问题也不会影响到主程序。

方法:在androidmanifest.xml的activity标签里加上android:process="packagename.web"就可以了

(2)WebView创建

方法:不在xml中定义 Webview,而是在需要的时候在Activity中创建,对传入WebView的Context使用使用ApplicationContext而不是ActivityContext。因为这样做可以在onDestory()里销毁掉webview及时清理内存;创建webview需要使用ApplicationContext而不是Activity的context,销毁时不再占有Activity对象;

LinearLayout mLayout = (LinearLayout) findViewById(R.id.layout);   
WebView mWebView = new WebView(getApplicationContext()); 
LinearLayout.LayoutParams params = new  LinearLayout.LayoutParams
    (ViewGroup.LayoutParams.MATCH_PARENT,  ViewGroup.LayoutParams.MATCH_PARENT);
mWebView.setLayoutParams(params); 
mLayout.addView(mWebView);

(3)WebView销毁

方法:在 Activity销毁WebView的时,需要在onDestroy()中:先让WebView加载null内容,再移除WebView,然后再将WebView.destroy(),最后WebView置空。

@Override 
protected void onDestroy() {  
    if (mWebView != null) {
        mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); 
        mWebView.clearHistory(); 
        mLayout.removeView(mWebView); 
         mWebView.destroy(); 
         mWebView = null; 
    } 
        super.onDestroy(); 
}

2、WebViewClient.onPageFinished()问题

无法确定当WebView调用这个方法的时候,网页内容是否真的加载完毕了。当前正在加载的网页产生跳转的时候这个方法可能会被多次调用,多数开发者都是参考的stackoverflow上如何侦听完成加载URL的WebView 这个上面的高票答案,但其中列举的解决方法并不完美。

解决方案:

当你的WebView需要加载各种各样的网页并且需要在页面加载完成时采取一些操作的话,可能WebChromeClient.onProgressChanged()比WebViewClient.onPageFinished()都要靠谱一些。

3、WebView后台耗电问题

在WebView加载页面的时候,WebView会自动开启线程去加载,如果没有很好地将WebView销毁的话,这些残余的线程会一直在后台运行,由此导致你的应用程序耗电量居高不下。

解决方案:

可以采用暴力的方法,即在Activity.onDestroy()中直接调用System.exit(0),使得应用程序完全被移出虚拟机。

在有的手机里,你如果webview加载的html里有一些js一直在执行比如动画之类的东西,如果此刻webview挂在了后台,这些资源是不会被释放 用户也无法感知,导致一直占有cpu 耗电特别快。

解决方案:

在Activity的onstop和onresume里分别把setJavaScriptEnabled();给设置成false和true。

4、WebView与JavaScript相互调用问题

如果是debug没有配置混淆时,调用时没问题的,但是当设置混淆后发现无法正常调用了。

解决方案:

在proguard-rules.pro中添加混淆。
-keepattributes *Annotation*  
-keepattributes *JavascriptInterface*
-keep public class xx.xxx.ShowLogJavaScriptInterface{
   public <methods>;
}
其中xx.xxx..ShowLogJavaScriptInterface 是不需要混淆的类

5、WebView加载图片失败问题

Android5.0以后的WebView加载的链接为Https开头,但是链接里面的内容,比如图片为Http链接,这时候,图片就会加载不出来。因为Android5.0上Webview默认不允许加载Http与Https混合内容:

**解决方案:**
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
    //mWebView.getSettings().setMixedContentMode(
                            WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
参数说明:
MIXED_CONTENT_ALWAYS_ALLOW 允许从任何来源加载内容,即使起源是不安全的;
MIXED_CONTENT_NEVER_ALLOW不允许Https加载Http的内容,即不允许从安全的起源去加载一个不安全的资源;
MIXED_CONTENT_COMPLTIBILITY_MODE当涉及到混合式内容时,WebView会尝试去兼容新Web浏览器的风格;

6、WebView开启硬件加速导致切换WebView闪屏问题

Android从3.0(API Level11)开始,在绘制View的时候支持硬件加速,充分利用GPU的特性,使得绘制更加平滑,但是会多消耗一些内存。开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是,当WebView视图被整体遮住一块,然后突然恢复时(比如使用SlideMenu将WebView从侧边滑出来时),这个过渡期会出现白块同时界面闪烁。再有就是如果在同一个ViewGroup中来回切换不同的WebView(包含了不同的网页内容)的话,就会发现闪屏是不可避免的。这应该是Android硬件加速的Bug,如果关闭硬件加速这种情况会好很多,但无法获得很好的浏览体验,会感觉网页滑动的时候一卡一卡的不跟手。

解决方案:

在过渡期前将WebView的硬件加速临时关闭,过渡期后再开启。

硬件加速分为四个级别:

Application级别
     <application android:hardwareAccelerated="true"...>
Activity级别
     <activity android:hardwareAccelerated="true"...>
window级别(目前为止,Android还不支持在Window级别关闭硬件加速。)
    getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
View级别
     view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
//过度前关闭硬件加速
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB){
    mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
//过度后开启硬件加速
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB){
    mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}

7、视频播放

在某些手机上,Webview有视频时,Activity销毁后,视频资源没有被销毁,甚至还能听到在后台播放。

解决办法:

在onDestory之前修改url为空地址。

mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); 

二、总结

以上就是在开发中遇到过WebView的一些坑,算是比较高频的。