该片文章讲述项目中遇到的问题,以及相关优化细节。
一、项目背景
1、项目需要支持hybrid应用,所以部分提供h5调用的通用js保存在客户端本地。优点有:访问速度快和无需额外数据流量。
2、项目主页都是h5形式,采用vue框架,支持离线缓存
3、使用CordovaWebView加载h5应用。
二、问题清单和优化
1、h5应用如何读到本地js文件?
针对第1个问题,很简单解决。webview提供拦截方法,只要和js端定义好import的格式,匹配好即可。代码编写如下:
拦截方法入口,系统版本不一致,调用方法不一样,请知晓。
/**
* URL拦截请求方法(API21以上)
* @param webView
* @param webResourceRequest 请求信息
* @return
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView webView, WebResourceRequest webResourceRequest) {
//21版本
//通过版本控制来将2个入口引入到一个拦截方法中
String method = webResourceRequest != null ? webResourceRequest.getMethod() : null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//OPTIONS请求跳过
if (method != null && method.equalsIgnoreCase("OPTIONS")) {
return null;
}
return customInterceptRequest(webView,
webResourceRequest.getUrl().toString(),
method,
webResourceRequest.getRequestHeaders(),
super.shouldInterceptRequest(webView, webResourceRequest));
}
return super.shouldInterceptRequest(webView, webResourceRequest);
}
/**
* URL拦截请求方法(API21以下)
* @param view
* @param url 请求地址
* @return
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
//11版本
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return customInterceptRequest(view, url, null, null, super.shouldInterceptRequest(view, url));
}
return super.shouldInterceptRequest(view, url);
}
匹配url,重新写回WebResourceResponse
/**
* 加载本地js文件
* @param url
* @param injection
* @return
*/
private WebResourceResponse handleInjectUrl(String url,String injection){
try{
String assetPath = url.substring(url.indexOf(injection) + injection.length(), url.length());
String cordovaPath = JS_PATH + assetPath;
InputStream fis = new FileInputStream(new File(cordovaPath));
return new WebResourceResponse(
"application/javascript",
"UTF-8",
fis
);
}catch (Exception e){
if (ModuleCommImpl.getInstance().isDebug()) {
LOG.e(TAG, "handleInjectUrl loading file fail.", e);
}
}
return new WebResourceResponse("application/javascript", "UTF-8", null);
}
此处有个优化点,非常耗时,第三点会解释,请继续阅读。
2、无网情况下如何继续访问数据?
首先h5应用需要支持离线缓存,使用localstorage把网络数据暂存起来。Android端需要把缓存开关打开。如图:
// Enable database
// We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
String databasePath = webView.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
settings.setDatabaseEnabled(true);
settings.setDatabasePath(databasePath);
// Enable AppCache
// Fix for CB-2282
settings.setCacheMode(WebSettings.LOAD_DEFAULT); // 默认使用缓存
settings.setAppCacheMaxSize(20 * 1048576);
settings.setAppCachePath(databasePath);
// 可以读取文件缓存(manifest生效)
settings.setAllowFileAccess(true);
settings.setAppCacheEnabled(true);
网上资料很多,查下即可。
3、加载首页数据缓慢?
h5应用往往遇到加载慢,体验不好的问题。在我们做了离线缓存时,稍微好点。但是此项目中遇到一个问题就是加载数据慢。
排查思路:
a、检查首页webview对象初始化时间
期初怀疑是对象初始化耗时,经打印时间,发现不是。
b、检查onPageStarted 和onPageFinished时间。
如果此处耗时比较大,就存在问题,当初我们这边耗时800ms,后来发现拦截代码,获取路径函数
public static File getCacheDirectory(Context context, boolean preferExternal, boolean persist) {
File appCacheDir = null;
String externalStorageState;
try {
externalStorageState = Environment.getExternalStorageState();
} catch (NullPointerException e) { // (sh)it happens (Issue #660)
externalStorageState = "";
}
该方法调用时间10ms,单次调用没什么问题。
对于我们加载本地js,比如本地60个js,针对浏览器,并行加载6个js,也就是加载所有的js需要总的时间:600ms,太费时了。
所以我们把路径写出
private static final String JS_PATH = ModuleCommImpl.getInstance().getFileJsPath()+"/plugin.apis/";
时间立马下降了。
此处一个优化点。后续开发者在做拦截时,需要考虑下。
c、页面没有渲染好的问题,需要h5优化。这里就不描述了。