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(!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());
}

猜你喜欢

转载自my.oschina.net/ososchina/blog/1799575