WebView三部曲:性能优化
WebView三部曲:性能优化
关于作者
郭孝星,程序员,吉他手,主要从事Android平台基础架构方面的工作,欢迎交流技术方面的问题,可以去我的Github提issue或者发邮件至guoxiaoxingse@163.com与我交流。
本系列文章讨论WebView的各种用法以及使用技巧, 一共包含三篇文章:
一 优化网页加载速度
默认情况html代码下载到WebView后,webkit开始解析网页各个节点,发现有外部样式文件或者外部脚本文件时,会异步发起网络请求下载文件,但如果
在这之前也有解析到image节点,那势必也会发起网络请求下载相应的图片。在网络情况较差的情况下,过多的网络请求就会造成带宽紧张,影响到css或
js文件加载完成的时间,造成页面空白loading过久。解决的方法就是告诉WebView先不要自动加载图片,等页面finish后再发起图片加载。
设置WebView, 先禁止加载图片
WebSettings webSettings = mWebView.getSettings();
//图片加载
if(Build.VERSION.SDK_INT >= 19){
webSettings.setLoadsImagesAutomatically(true);
}else {
webSettings.setLoadsImagesAutomatically(false);
}
覆写WebViewClient的onPageFinished()方法, 页面加载结束后再加载图片
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!view.getSettings().getLoadsImagesAutomatically()) {
view.getSettings().setLoadsImagesAutomatically(true);
}
}
注意: 4.4以上系统在onPageFinished时再恢复图片加载时,如果存在多张图片引用的是相同的src时,会只有一个image标签得到加载,因而对于这样的系统我们就先直接加载。
二 硬件加速页面闪烁问题
4.0以上的系统我们开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是,当WebView视图被整体遮住一块,然后突然恢复时(比如使用SlideMenu将WebView从侧边
滑出来时),这个过渡期会出现白块同时界面闪烁。解决这个问题的方法是在过渡期前将WebView的硬件加速临时关闭,过渡期后再开启,如下所示:
过度前关闭硬件加速
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);
}
以上三篇, 大致就说这么多, 在实际的项目中我们通常会自己去封装一个H5Activity用来统一显示H5页面, 下面就提供了完整的H5Activity, 封装了WebView各种特性与jockeyjs代码交互。
该H5Activity提供WebView常用设置、H5页面解析、标题解析、进度条显示、错误页面展示、重新加载等功能。可以拿去稍作改造, 用于自己的项目中。
package com.guoxiaoxing.webview;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import com.jockeyjs.Jockey;
import com.jockeyjs.JockeyImpl;
/**
* Author: guoxiaoxing
* Date: 16/7/21 下午4:03
* Function: H5Activity 提供各种功能载入Web页面
* <p>
* For more information, you can visit https://github.com/guoxiaoxing or contact me by
* guoxiaoxingv@163.com
*/
public class H5Activity extends AppCompatActivity {
public static final String H5_URL = "H5_URL";
private static final String JOCKEY_EVENT_NAME = "JOCKEY_EVENT_NAME";
private static final String TAG = H5Activity.class.getSimpleName();
private Toolbar mToolbar;
private ProgressBar mProgressBar;
private Jockey mJockey;
private WebView mWebView;
private WebViewClient mWebViewClient;
private WebChromeClient mWebChromeClient;
private String mUrl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_h5);
setupView();
setupSettings();
}
@Override
protected void onStart() {
super.onStart();
setupJockey();
setupData();
}
private void setupView() {
mToolbar = (Toolbar) findViewById(R.id.h5_toolbar);
mProgressBar = (ProgressBar) findViewById(R.id.h5_progressbar);
mWebView = (WebView) findViewById(R.id.h5_webview);
}
private void setupSettings() {
mWebView.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY);
mWebView.setHorizontalScrollBarEnabled(false);
mWebView.setOverScrollMode(WebView.OVER_SCROLL_NEVER);
WebSettings mWebSettings = mWebView.getSettings();
mWebSettings.setSupportZoom(true);
mWebSettings.setLoadWithOverviewMode(true);
mWebSettings.setUseWideViewPort(true);
mWebSettings.setDefaultTextEncodingName("utf-8");
mWebSettings.setLoadsImagesAutomatically(true);
//JS
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebSettings.setAllowFileAccess(true);
mWebSettings.setUseWideViewPort(true);
mWebSettings.setDatabaseEnabled(true);
mWebSettings.setLoadWithOverviewMode(true);
mWebSettings.setDomStorageEnabled(true);
//缓存
ConnectivityManager connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if (info != null && info.isConnected()) {
String wvcc = info.getTypeName();
Log.d(TAG, "current network: " + wvcc);
mWebSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
} else {
Log.d(TAG, "No network is connected, use cache");
mWebSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
if (Build.VERSION.SDK_INT >= 16) {
mWebSettings.setAllowFileAccessFromFileURLs(true);
mWebSettings.setAllowUniversalAccessFromFileURLs(true);
}
if (Build.VERSION.SDK_INT >= 12) {
mWebSettings.setAllowContentAccess(true);
}
setupWebViewClient();
setupWebChromeClient();
}
private void setupJockey() {
mJockey = JockeyImpl.getDefault();
mJockey.configure(mWebView);
mJockey.setWebViewClient(mWebViewClient);
mJockey.setOnValidateListener(new Jockey.OnValidateListener() {
@Override
public boolean validate(String host) {
return "yourdomain.com".equals(host);
}
});
//TODO set your event handler
mJockey.on(JOCKEY_EVENT_NAME, new EventHandler());
}
private void setupData() {
mUrl = getIntent().getStringExtra(H5_URL);
if (TextUtils.isEmpty(mUrl)) {
//TODO show error page
} else {
mWebView.loadUrl(mUrl);
}
}
private void setupWebViewClient() {
mWebViewClient = new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//TODO 处理URL, 例如对指定的URL做不同的处理等
return false;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
}
};
mWebView.setWebViewClient(mWebViewClient);
}
private void setupWebChromeClient() {
mWebChromeClient = new WebChromeClient() {
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
mToolbar.setTitle(title);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
mProgressBar.setProgress(newProgress);
if (newProgress == 100) {
mProgressBar.setVisibility(View.GONE);
} else {
mProgressBar.setVisibility(View.VISIBLE);
}
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
};
mWebView.setWebChromeClient(mWebChromeClient);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}