Android Webview 性能优化

释放双眼,带上耳机,听听看~!

1、内存泄漏解决方法

复写Webview,实现如下方法

 
 public MyWebView(Context context, AttributeSet attrs) {
        super(context, attrs, android.R.attr.webViewStyle);
        setBackgroundColor(Color.TRANSPARENT);
        // 删除掉Android默认注册的JS接口
        removeDefaultJavascriptInterface();
        WindowManager  wm= (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        setConfigCallback(wm);
   }
 
   @Override
    public void destroy() {
        getSettings().setJavaScriptEnabled(false);
        this.clearFormData();
        this.clearHistory();
        this.destoryWebView();
        super.destroy();
    }

    private void destoryWebView() {
        this.stopLoading();
        this.removeAllViews();
        if(this.getParent()!=null){
            //处理webview无法释放造成的内存泄漏,必须在destroy之前调用
            ViewGroup parent = (ViewGroup) this.getParent();
            parent.removeView(this);
            setConfigCallback(null);
        }

    }

    public void setConfigCallback(WindowManager windowManager) {
        try {
                if(Build.VERSION.SDK_INT>15) {
                    return;
                }
                Field field = WebView.class.getDeclaredField("mWebViewCore");
                field = field.getType().getDeclaredField("mBrowserFrame");
                field = field.getType().getDeclaredField("sConfigCallback");
                field.setAccessible(true);
                Object configCallback = field.get(null);
                if (null == configCallback) {
                    return;
                }
                field = field.getType().getDeclaredField("mWindowManager");
                field.setAccessible(true);
                field.set(configCallback, windowManager);
        } catch(Exception e) {
            e.printStackTrace();
            return;
        }
    }

参考:

  • https://www.jianshu.com/p/eada9b652d99
  • https://blog.csdn.net/xygy8860/article/details/53334476?utm_source=itdadao&utm_medium=referral
  • https://stackoverflow.com/questions/11995270/error-webview-destroy-called-while-still-attached/12408703#12408703
  • https://www.jianshu.com/p/c2412918b2b5
  • org.chromium.android_webview.AwContents源码https://github.com/pwnall/chromeview/blob/master/src/org/chromium/android_webview/AwContents.java
  • https://chromium.googlesource.com/chromium/src/android_webview/glue/+/master/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java

 

2、SSL证书处理

https://blog.csdn.net/zoeice/article/details/13996579

 

3、常见问题处理

史上最全WebView使用,附送Html5Activity一份

 

4.onPageFinished被调用多次

使用onPageProgressChanged代替

private void  handleProgress(WebView view, int newProgress){
    if(progressPending.get()!=newProgress){
        progressPending.set(newProgress);
        onProgressChanged(newProgress);

    }
}
@Override
public final void onProgressChanged(WebView view, int newProgress) {
    super.onProgressChanged(view, newProgress);
    handleProgress(view,newProgress);
}

public void onProgressChanged(int newProgress){
   Log.i("WebChromeClient","progress="+newProgress+"%");
   if(newProgress==100){
       Log.i("WebChromeClient","加载完成");
   }
}

5.WebChromeClient接口中onReceiveTitle返回的标题是url

解决方法如下

@Override
public final void onReceivedTitle(WebView view, String title) {
    super.onReceivedTitle(view, title);
    handleProgress(view,view.getProgress());
    if(URLUtils.isNetworkUrl(title)){
      return; 
   }
    if(!TextUtils.isEmpty(title) && !TextUtils.isEmpty(view.getUrl())&& !view.getUrl().contains(title)){
        onReceivedTitle(title);
    }
}

public void onReceivedTitle( String title){

}

6.使用ApplicationContext

使用ApplicationContext可以减少对当前Activity的依赖,便于内存释放,但是这里还有一些问题需要处理,比如ApplicationContext不能加载主题,必须通过ContextThemeWrapper加载。此外,如果使用了ApplicationContext时没有设置WebChromeClient的情况下,alert,confirm等对话框无法显示。

无法显示的原因可以参考源码:

https://chromium.googlesource.com/chromium/src/android_webview/glue/+/master/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java

 private boolean showDefaultJsDialog(JsPromptResult res, int jsDialogType, String defaultValue,
            String message, String url) {
        如果Context不是一个Activity是不允许显示的
        Context activityContext = AwContents.activityFromContext(mContext);
        if (activityContext == null) {
            Log.w(TAG, "Unable to create JsDialog without an Activity");
            return false;
        }
        try {
            new JsDialogHelper(res, jsDialogType, defaultValue, message, url)
                    .showDialog(activityContext);
        } catch (WindowManager.BadTokenException e) {
            Log.w(TAG,
                    "Unable to create JsDialog. Has this WebView outlived the Activity it was created with?");
            return false;
        }
        return true;
    }

继承关系图

如果h5页面不适用alert,confirm等,可以使用ApplicationContext,否则建议使用activitiy或者设置WebChromeClient自行实现。这里提供一个使用ApplicaitonContext的例子。

public MyWebView(Context context, AttributeSet attrs) {
    super(WebThemeContext.wrapper(context), attrs, android.R.attr.webViewStyle);
}
public class WebThemeContext extends ContextThemeWrapper {

    public WebThemeContext ( Context context) {
        super(MyApplication.getInstance(), context.getTheme());  //设置主题
    }

    public static WebThemeContext wrapper( Context context){
        return new WebThemeContext(context);
    }
    @Override
    public Context getApplicationContext() {
        return getInstance.getInstance();
    }


}

 7.性能监控

https://blog.csdn.net/lmj623565791/article/details/58626355

8.Android WebView 输入框键盘不弹出

在复写MyView时,使用的主题id必须设置,并且设置为android.R.attr.webViewStyle,否则无法调用native服务,如键盘

9.Android Webview是否应该开启硬件加速

由于碎片化问题太多,建议保持默认状态【默认表示由系统决定,不要手动设置】,否则可能产生问题。

 

10.Cookie同步导致的内存泄漏

使用CookieSyncManager同步时,会永久引用第一个acitivity的的Context,为了避免此种情况,请使用ApplicationContext

 if (Build.VERSION.SDK_INT < 21) {
        android.webkit.CookieSyncManager.createInstance(context.getApplicationContext());
}

 

11. 30x重定向问题

Android Webview重定向可能导致Android 5.0和5.0之前的Webview内核死锁,而且会导致资源无法释放。一旦内核死锁,那么整个app必须重启才能加载网页,否则一直处于空白状态。

 

12.loadUrl(url,map)方法加载带hash(带#号)的url导致刷新问题或请求头缓存问题。

① 如果调用loadUrl(url,map)方法去加载资源,那么在此调用loadUrl(ur),reload,loadUrl(url,map)造成无法刷新的问题。这个现象主要出现在Android 8.0的系统中。

可尝试调用如下url尝试:

https://baike.baidu.com/item/%E9%83%8E%E5%B9%B3/58857#/
https://baike.baidu.com/item/%E9%83%8E%E5%B9%B3/58857#/?a=123
https://baike.baidu.com/item/%E9%83%8E%E5%B9%B3/58857#3

②loadUrl(url,map) 第二个参数map中传入的数据用于请求头,此外这个请求头数据会被webview缓存下来,刷新时,请求头中的数据还是原来的,因此,不适用传入需要进程变化的“状态”信息。

 

解决方法:不要使用loadUrl(url,map),推荐使用loadUrl(url),如果非要传输参数,还不如在url中添加参数。

 

13.Android 4.2.2 系统注入javascript脚本导致页面循环刷新,导致内核闪退。

Android 4.2.2进行了一次内核调整,但是遗留了一部分bug,就是在js脚本注入的时候,必须得添加 “javascript:”前缀,否则页面将无限刷新,最后ANR。因此,这里建议任何脚本,必须添加 “javscript:”前缀。

 

14.copyBackForwardList调用时引起空指针问题

部分手机上调用copyBackForwardList会出现空指针问题,需要处理

人已赞赏
Android文章

Android APP native 崩溃分析之令人困惑的 backtrace

2019-10-25 13:22:48

Android文章

Android Context对象继承分析

2019-10-25 14:21:15

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索