版本 |
内核 |
描述 |
<19(Android 4.4) |
Android Webkit内核 |
对HTML5的支持不是很好,js存在安全漏洞 |
>=19
|
Chromium内核 |
|
Android5.0开始 |
|
WebView移植成了一个独立的apk |
Android7.0 |
|
安装Chrome (version>51),那么Chrome将会直接为应用的WebView提供渲染,WebView版本会随着Chrome的更新而更新,用户也可以选择WebView的服务提供方(在开发者选项->WebView Implementation里),WebView可以脱离应用,在一个独立的沙盒进程中渲染页面
|
Android8.0 |
|
默认开启WebView多进程模式,即WebView运行在独立的沙盒进程中 |
public static void setDefaultWebSettings(WebView webView) {
WebSettings webSettings = webView.getSettings();
//允许js代码
webSettings.setJavaScriptEnabled(true);
//禁用放缩
webSettings.setDisplayZoomControls(false);
webSettings.setBuiltInZoomControls(false);
//禁用文字缩放
webSettings.setTextZoom(100);
//设置浏览器缓存
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
//缓存模式如下:
//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
//5.0以上开启混合模式加载
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//是否使用预览模式加载界面
webSettings.setLoadWithOverviewMode(true);
//设置WebView是否使用viewport
webSettings.setUseWideViewPort(true);
//允许SessionStorage/LocalStorage存储
webSettings.setDomStorageEnabled(true);
//10M缓存,api 18后,系统自动管理。
webSettings.setAppCacheMaxSize(10 * 1024 * 1024);
//允许App缓存
webSettings.setAppCacheEnabled(true);
//设置App缓存地址
webSettings.setAppCachePath(context.getDir("appcache", 0).getPath());
//允许WebView使用File协议
webSettings.setAllowFileAccess(true);
//不保存密码
webSettings.setSavePassword(false);
//设置UA
webSettings.setUserAgentString(webSettings.getUserAgentString() + "kaolaApp/" + AppUtils.getVersionName());
//移除部分系统JavaScript接口
KaolaWebViewSecurity.removeJavascriptInterfaces(webView);
//自动加载图片
webSettings.setLoadsImagesAutomatically(true);
}
处理各种通知、请求事件
常用方法:
辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。
常用方法:
Android WebView自带的缓存机制有5种:
https://blog.csdn.net/carson_ho/article/details/71402764
WebSettings.setJavaScriptEnabled(true);在Android 4.4(<19)以下版本存在安全漏洞,
如果启用了JavaScript,务必做好安全措施,防止远程执行漏洞
@TargetApi(11)
private static final void removeJavascriptInterfaces(WebView webView) {
try {
if (Build.VERSION.SDK_INT >= 11 && Build.VERSION.SDK_INT < 17) {
webView.removeJavascriptInterface("searchBoxJavaBridge_");
webView.removeJavascriptInterface("accessibility");
webView.removeJavascriptInterface("accessibilityTraversal");
}
} catch (Throwable tr) {
tr.printStackTrace();
}
}
在Android 4.4上通过注解@JavascriptInterface方式建立Javascript对象和android原生对象的绑定
webView.evaluateJavascript("javascript:Date.now()", new ValueCallback() {
@Override
public void onReceiveValue(String value) {
System.out.println(value);
}
});
webView.addJavascriptInterface(new JSInterface(),"android");
class JSInterface{
@JavascriptInterface
public void getUserInfo(){}
@JavascriptInterface
public void getDeviceInfo(){}
}
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.equals('sdk:hello')) {
System.out.println('hello world');
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
private class hijackWebChromeClient extends WebChromeClient {
@Override
public boolean onJsPrompt(WebView view,String url, String message, String defaultValue, JsPromptResult result) {
if (this.hijack(message)) {
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
}
//注入劫持回调类
WebView webview = (WebView) findViewById(R.id.webview);
webview.loadUrl('http://imnerd.org');
webview.setWebChromeClient(new hijackChromeClient);
以上讲述了 JS 调用客户端的方法,以及客户端调用前端的方法。除了这两种单向调用的方式之外,往往比较多的是 JS 调用客户端方法,客户端再调用 JS 返回结果的双向调用。在 JS 调用的时候需要传入一个回调方法名,然后客户端直接执行回调方法。
该项目在Java和JavaScript之间架起了一座桥梁。它提供了从js调用Java代码并从java调用js代码的安全方便的方法。
传送门:https://github.com/lzyzsd/JsBridge
当然也有很多其他的方案,但思想大都一致。
Web具有快速迭代发布的天然优势,但也存在中一些让人诟病的问题,比如加载速度慢,体验差等。但现在有很多优化框架来解决这些问题,优化后虽说不能媲美Native,但也不是那么差,当然这个过程需要前后端的配合。
VasSonic:轻量级高性能Hybrid框架
CandyWebCache是移动端web资源的本地缓存解决方案
如何缩短这些过程的时间,就成了优化WebView性能的关键。优化主要从webview的初始化、加载、渲染这几个方向进行
WebView初始化
初始化webview设计到浏览器内核初始化,还只能在主线程中执行。
首次初始化时间 |
二次初始化时间 |
403ms |
22.5ms |
解决方案:提前初始化,并创建一个WebView缓存池(需要多个webview做转场动画)
public class GMWebViewPool {
/**
* 创建WebView实例
* 用了applicationContext
*/
public void prepareNewWebView(Context context) {
if (mCachedWebViewStack.size() < CACHED_WEBVIEW_MAX_NUM) {
mCachedWebViewStack.push(new GMWebView(new MutableContextWrapper(context.getApplicationContext())));
}
}
/**
* 从缓存池中获取合适的WebView
*
* @param context activity context
* @return WebView
*/
public GMWebView acquireWebViewInternal(Context context) {
// 为空,直接返回新实例
if (mCachedWebViewStack == null || mCachedWebViewStack.isEmpty()) {
return new GMWebView(context);
}
GMWebView webView = mCachedWebViewStack.pop();
// webView不为空,则开始使用预创建的WebView,并且替换Context
MutableContextWrapper contextWrapper = (MutableContextWrapper) webView.getContext();
contextWrapper.setBaseContext(context);
return webView;
}
}
MutableContextWrapper,作为Context的一个中间层。我们会将Activity context包在MutableContextWrapper里面,destory的时候,会将WebView的Context设置为Application的Context,从而释放Activity Context。
//precreate WebView
MutableContextWrapper contextWrapper = new MutableContextWrapper(BaseApplicationImpl.sApplication);
mPool[0] = new WebView(contextWrapper);
//reset WebView
ct =(MutableContextWrapper)webview.getContext();
ct.setBaseContext(getApplication());
//reuse WebView
((MutableContextWrapper)webview.getContext()).setBaseContext(activityContext);
优化后:
WebView初始化 |
3ms |