您的位置:首页 > 移动开发

Android Webview 性能优化

2018-04-23 11:27 513 查看

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:"前缀。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: