Android产品研发(十八)-->webview问题集锦
2016-11-03 21:28
375 查看
上一篇文章中我们介绍了hybrid开发相关的知识。重点介绍了hybrid开发的概念,hybrid开发的作用,Android中如何实现hybrid开发,Android中实现hybrid开发的例子,以及产品开发中hybrid开发实践等,通过对以上这些概念的介绍我们对hybrid开发应该已经有了大概的了解,更多具体的内容可参考我的:Android产品研发(十七)–>Hybrid开发
本文中我们将介绍一下Android中webview在使用过程中会遇到的一些问题。这些问题主要是webview在使用过程中我已经趟过的坑,希望通过这篇文章的介绍能够帮助大家更好的使用webview。
下面是本文主要介绍的一些知识点,后续使用过程中可能会有更新。
最近App中相当一部分的页面内容使用的是webview。而使用webview加载页面一个需要注意的地方就是性能,所以最近也研究了一下webview的性能优化问题。
webview的性能问题
在讲解webview的性能问题之前,我们先来了解一下Android webview的缓存机制。
WebView中存在着两种缓存:网页数据缓存(网页数据,url等)、H5缓存(H5代码缓存数据)
不同的缓存数据会保存在不同的文件目录下,这里引用一下其他blog的说法:
webview中也是可以设置缓存是否可用的,一般是通过WebSettings对象设置,下面我们就来看一下WebSettings对象的使用。
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setAllowFileAccess(false);
webSettings.setUseWideViewPort(false);
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
webSettings.setDatabaseEnabled(false);
webSettings.setAppCacheEnabled(false);
webSettings.setBlockNetworkImage(true);
// 设置WebView的Client
mWebView.setWebViewClient(new MWebViewClient(this));
// 设置可现实js的alert弹窗
mWebView.setWebChromeClient(new WebChromeClient());
可以看到我们可以使用WebSettings对象设置缓存是否可用,缓存DB是否可用等。我们需要首先确保这里设置了缓存可用,才可以继续设置使用何种缓存策略。
下面我们来看一下webview的五种缓存模式:
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
(1)使用LOAD_CACHE_ELSE_NETWORK缓存模式,这样需要在APP退出的时候清除webview缓存,但是这样做有一个弊端就是如果当前App已经是打开状态,网页内容有更新的话不会看到;
(2)使用LOAD_DEFAULT这种缓存方式,数据从缓存中获取还是从网络中获取根据H5页面的参数判断,这样做的好处是可以动态的处理更新内容;
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setRenderPriority(RenderPriority.HIGH); mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); //设置 缓存模式
// 开启 DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
//开启 database storage API 功能
mWebView.getSettings().setDatabaseEnabled(true);
String cacheDirPath = getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME;
// String cacheDirPath = getCacheDir().getAbsolutePath()+Constant.APP_DB_DIRNAME;
Log.i(TAG, “cacheDirPath=”+cacheDirPath);
//设置数据库缓存路径
mWebView.getSettings().setDatabasePath(cacheDirPath);
//设置 Application Caches 缓存目录
mWebView.getSettings().setAppCachePath(cacheDirPath);
//开启 Application Caches 功能
mWebView.getSettings().setAppCacheEnabled(true);
//清理Webview缓存数据库
try {
deleteDatabase(“webview.db”);
deleteDatabase(“webviewCache.db”);
} catch (Exception e) {
e.printStackTrace();
}
网页在加载的时候暂时不加载图片,当所有的HTML标签加载完成时在加载图片具体的做法如下初始化webview的时候设置不加载图片
webSettings.setBlockNetworkImage(true);
然后在html标签加载完成之后在加载图片内容:
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mWebView.getSettings().setBlockNetworkImage(false);
}
O(∩_∩)O哈哈~,这样做的好处就是可以给人的感觉网页加载速度很快…
加载网页内容时,在加载完成html后替换页面内容引用的地址改为本地的资源文件地址,这样可以直接加载本地的资源文件加快资源的访问速度,目前主流的新闻客户端访问webview时多采用这种方式。
好吧,讲解完了webview的性能优化问题之后我们在讲解一下如何在H5页面种入Cookie信息。
H5页面种入Cookie问题
app中存在webview控件,既可以通过Native与js代码交互的方式实现信息的交互也可以通过cookie的方式与实现Native与H5端的交互,查询的好多资料各种各样的实现方式都有,最终不断尝试基本实现了需求,现说明一下最终的实现方式;
/**
* 客户端将cookie种入H5页面中,H5页面可以通过js代码实现对native种入cookie信息的读取操作
*/
public static void synCookies(Context context, String url, String cookie) {
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
其中CookieManager是cookie的管理对象,主要实现对网页cookie的注入与清除等工作。注入字符串的形式是:key=value;domain=url的形式(其中url为需要注入cookie的url链接地址)
那么如何移除cookie呢?
/**
* 移除cookie
*/
public static void removeCookies(Context context) {
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeSessionCookie();
一般情况下都是在打开H5页面的时候种入Cookie信息,然后在离开H5页面的时候清除cookie信息。当然了通过cookie实现native与js的交互只是实现信息交互的一种方式,我们还可以通过js与Java代码相互调用的方式实现相互交互,文章的后面会有介绍。
而下面我们再来讲解一下activity退出时webview报错的问题。
Activity退出时webview报错的问题
前段时间在调试代码的时候,有一段关于webview的代码,即退出Fragment的时候清除webview,这时候在其他手机上是没有问题的,但是在三星Grand2中报错,而其报错信息是:
java.lang.Throwable: Error: WebView.destroy() called while still attached!
at Android.webkit.WebViewClassic.destroy(WebViewClassic.java:4173)
at Android.webkit.WebView.destroy(WebView.java:707)
at com.youyou.uuelectric.renter.UI.web.H5Fragment.onDestroyView(H5Fragment.java:202)
at Android.support.v4.app.Fragment.performDestroyView(Fragment.java:2167)
at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1141)
at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248)
at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1230)
at Android.support.v4.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:2079)
at Android.support.v4.app.FragmentController.dispatchDestroy(FragmentController.java:235)
at Android.support.v4.app.FragmentActivity.onDestroy(FragmentActivity.java:326)
at Android.support.v7.app.AppCompatActivity.onDestroy(AppCompatActivity.java:161)
at com.youyou.uuelectric.renter.UI.base.BaseActivity.onDestroy(BaseActivity.java:136)
at Android.app.Activity.performDestroy(Activity.java:5543)
at Android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1134)
at Android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3637)
at Android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3672)
at Android.app.ActivityThread.access1300(ActivityThread.java:168)atAndroid.app.ActivityThreadH.handleMessage(ActivityThread.java:1382)
at Android.os.Handler.dispatchMessage(Handler.java:99)
at Android.os.Looper.loop(Looper.java:176)
at Android.app.ActivityThread.main(ActivityThread.java:5493)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
at dalvik.system.NativeStart.main(Native Method)
程序也没有异常退出之类的动作,清除webview的代码是这样写的:
@Override
public void onDestroyView() {
super.onDestroyView();
mWebView.removeAllViews();
mWebView.destroy();
}
这个错误大概的意思是:当你结束webview的时候,webview还依附在其父控件之下,应当在调用webview.destory()方法之前接触他们之间的依附关系,那么既然错误提示信息已经很明显了,我们就根据错误信息尝试着首先执行webview父控件的清除工作,然后在执行webview控件的清除操作,所以代码中应该这样实现:
@Override
public void onDestroyView() {
super.onDestroyView();
swipeRefreshLayout.removeView(mWebView);
mWebView.removeAllViews();
mWebView.destroy();
}
这样经过修改之后特定机型上关于webview的错误就不在了。
webview中实现Native与js相互调用
上面我们介绍的通过cookie实现Android native与H5的信息交互只是一种方式,我们也可以通过java代码与js代码直接相互调用的方式实现Android native与H5信息的相互,这里简单的介绍一下使用方式
(1)在H5页面中添加一个js函数
本文中我们将介绍一下Android中webview在使用过程中会遇到的一些问题。这些问题主要是webview在使用过程中我已经趟过的坑,希望通过这篇文章的介绍能够帮助大家更好的使用webview。
下面是本文主要介绍的一些知识点,后续使用过程中可能会有更新。
webview的性能优化 webview注入cookie信息 webview退出activity异常 webview中native与js交互 webview下载文件 腾讯X5浏览服务
最近App中相当一部分的页面内容使用的是webview。而使用webview加载页面一个需要注意的地方就是性能,所以最近也研究了一下webview的性能优化问题。
webview的性能问题
在讲解webview的性能问题之前,我们先来了解一下Android webview的缓存机制。
Android WebView缓存机制
WebView中存在着两种缓存:网页数据缓存(网页数据,url等)、H5缓存(H5代码缓存数据)
不同的缓存数据会保存在不同的文件目录下,这里引用一下其他blog的说法:
当我们加载Html时候,会在我们data/应用package下生成database与cache两个文件夹: 我们请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webviewCache文件夹下。 这里写图片描述
webview中也是可以设置缓存是否可用的,一般是通过WebSettings对象设置,下面我们就来看一下WebSettings对象的使用。
Android中webview组件有几个重要的方法
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setAllowFileAccess(false);
webSettings.setUseWideViewPort(false);
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
webSettings.setDatabaseEnabled(false);
webSettings.setAppCacheEnabled(false);
webSettings.setBlockNetworkImage(true);
// 设置WebView的Client
mWebView.setWebViewClient(new MWebViewClient(this));
// 设置可现实js的alert弹窗
mWebView.setWebChromeClient(new WebChromeClient());
1 2 3 4 5 6 7 8 9 10 11 12 13 1 2 3 4 5 6 7 8 9 10 11 12 13
可以看到我们可以使用WebSettings对象设置缓存是否可用,缓存DB是否可用等。我们需要首先确保这里设置了缓存可用,才可以继续设置使用何种缓存策略。
下面我们来看一下webview的五种缓存模式:
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
几种缓存方式的实现
(1)使用LOAD_CACHE_ELSE_NETWORK缓存模式,这样需要在APP退出的时候清除webview缓存,但是这样做有一个弊端就是如果当前App已经是打开状态,网页内容有更新的话不会看到;
(2)使用LOAD_DEFAULT这种缓存方式,数据从缓存中获取还是从网络中获取根据H5页面的参数判断,这样做的好处是可以动态的处理更新内容;
设置缓存
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setRenderPriority(RenderPriority.HIGH); mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); //设置 缓存模式
// 开启 DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
//开启 database storage API 功能
mWebView.getSettings().setDatabaseEnabled(true);
String cacheDirPath = getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME;
// String cacheDirPath = getCacheDir().getAbsolutePath()+Constant.APP_DB_DIRNAME;
Log.i(TAG, “cacheDirPath=”+cacheDirPath);
//设置数据库缓存路径
mWebView.getSettings().setDatabasePath(cacheDirPath);
//设置 Application Caches 缓存目录
mWebView.getSettings().setAppCachePath(cacheDirPath);
//开启 Application Caches 功能
mWebView.getSettings().setAppCacheEnabled(true);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 退出App时清除缓存
//清理Webview缓存数据库
try {
deleteDatabase(“webview.db”);
deleteDatabase(“webviewCache.db”);
} catch (Exception e) {
e.printStackTrace();
}
//WebView 缓存文件 File appCacheDir = new File(getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME); Log.e(TAG, "appCacheDir path="+appCacheDir.getAbsolutePath()); File webviewCacheDir = new File(getCacheDir().getAbsolutePath()+"/webviewCache"); Log.e(TAG, "webviewCacheDir path="+webviewCacheDir.getAbsolutePath()); //删除webview 缓存目录 if(webviewCacheDir.exists()){ deleteFile(webviewCacheDir); } //删除webview 缓存 缓存目录 if(appCacheDir.exists()){ deleteFile(appCacheDir); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 其他的缓存策略
网页在加载的时候暂时不加载图片,当所有的HTML标签加载完成时在加载图片具体的做法如下初始化webview的时候设置不加载图片
webSettings.setBlockNetworkImage(true);
1 1
然后在html标签加载完成之后在加载图片内容:
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mWebView.getSettings().setBlockNetworkImage(false);
}
1 2 3 4 5 1 2 3 4 5
O(∩_∩)O哈哈~,这样做的好处就是可以给人的感觉网页加载速度很快…
将网页内容中需要的js,css引用文件保存在App本地
加载网页内容时,在加载完成html后替换页面内容引用的地址改为本地的资源文件地址,这样可以直接加载本地的资源文件加快资源的访问速度,目前主流的新闻客户端访问webview时多采用这种方式。
好吧,讲解完了webview的性能优化问题之后我们在讲解一下如何在H5页面种入Cookie信息。
H5页面种入Cookie问题
app中存在webview控件,既可以通过Native与js代码交互的方式实现信息的交互也可以通过cookie的方式与实现Native与H5端的交互,查询的好多资料各种各样的实现方式都有,最终不断尝试基本实现了需求,现说明一下最终的实现方式;
/**
* 客户端将cookie种入H5页面中,H5页面可以通过js代码实现对native种入cookie信息的读取操作
*/
public static void synCookies(Context context, String url, String cookie) {
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
Uri uri = null; String domain = ""; try { uri = Uri.parse(URLDecoder.decode(url, "utf-8")); domain = uri.getHost(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String cookieStr = cookieManager.getCookie(url); // 判断token是否发生变化,发生变化的话则更新cookie L.i("cookieEquce:" + cookie.equals(H5Constant.TOKEN + "=")); if (!TextUtils.isEmpty(cookieStr) && cookieStr.contains(cookie) && !cookie.equals(H5Constant.TOKEN + "=")) { return; } // 更新domain(不再从UserInfo中获取,更改为从UrlInfo中获取) if (!TextUtils.isEmpty(URLConfig.getUrlInfo().getB4Domain())) { domain = URLConfig.getUrlInfo().getB4Domain(); } List<String> uaList = SysConfig.getSystemUa(context); String md = ";domain=" + domain; // 添加经纬度信息到Cookie中 cookieManager.setCookie(url, "lat=" + Config.lat + md); cookieManager.setCookie(url, "lng=" + Config.lng + md); cookieManager.setCookie(url, cookie + ";" + md); if (uaList != null && uaList.size() > 0) { for (String coo : uaList) { cookieManager.setCookie(url, coo + md); } } CookieSyncManager.getInstance().sync(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
其中CookieManager是cookie的管理对象,主要实现对网页cookie的注入与清除等工作。注入字符串的形式是:key=value;domain=url的形式(其中url为需要注入cookie的url链接地址)
那么如何移除cookie呢?
/**
* 移除cookie
*/
public static void removeCookies(Context context) {
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeSessionCookie();
CookieSyncManager.getInstance().sync(); } 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
一般情况下都是在打开H5页面的时候种入Cookie信息,然后在离开H5页面的时候清除cookie信息。当然了通过cookie实现native与js的交互只是实现信息交互的一种方式,我们还可以通过js与Java代码相互调用的方式实现相互交互,文章的后面会有介绍。
而下面我们再来讲解一下activity退出时webview报错的问题。
Activity退出时webview报错的问题
前段时间在调试代码的时候,有一段关于webview的代码,即退出Fragment的时候清除webview,这时候在其他手机上是没有问题的,但是在三星Grand2中报错,而其报错信息是:
java.lang.Throwable: Error: WebView.destroy() called while still attached!
at Android.webkit.WebViewClassic.destroy(WebViewClassic.java:4173)
at Android.webkit.WebView.destroy(WebView.java:707)
at com.youyou.uuelectric.renter.UI.web.H5Fragment.onDestroyView(H5Fragment.java:202)
at Android.support.v4.app.Fragment.performDestroyView(Fragment.java:2167)
at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1141)
at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248)
at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1230)
at Android.support.v4.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:2079)
at Android.support.v4.app.FragmentController.dispatchDestroy(FragmentController.java:235)
at Android.support.v4.app.FragmentActivity.onDestroy(FragmentActivity.java:326)
at Android.support.v7.app.AppCompatActivity.onDestroy(AppCompatActivity.java:161)
at com.youyou.uuelectric.renter.UI.base.BaseActivity.onDestroy(BaseActivity.java:136)
at Android.app.Activity.performDestroy(Activity.java:5543)
at Android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1134)
at Android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3637)
at Android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3672)
at Android.app.ActivityThread.access1300(ActivityThread.java:168)atAndroid.app.ActivityThreadH.handleMessage(ActivityThread.java:1382)
at Android.os.Handler.dispatchMessage(Handler.java:99)
at Android.os.Looper.loop(Looper.java:176)
at Android.app.ActivityThread.main(ActivityThread.java:5493)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
at dalvik.system.NativeStart.main(Native Method)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
程序也没有异常退出之类的动作,清除webview的代码是这样写的:
@Override
public void onDestroyView() {
super.onDestroyView();
mWebView.removeAllViews();
mWebView.destroy();
}
1 2 3 4 5 6 1 2 3 4 5 6
这个错误大概的意思是:当你结束webview的时候,webview还依附在其父控件之下,应当在调用webview.destory()方法之前接触他们之间的依附关系,那么既然错误提示信息已经很明显了,我们就根据错误信息尝试着首先执行webview父控件的清除工作,然后在执行webview控件的清除操作,所以代码中应该这样实现:
@Override
public void onDestroyView() {
super.onDestroyView();
swipeRefreshLayout.removeView(mWebView);
mWebView.removeAllViews();
mWebView.destroy();
}
1 2 3 4 5 6 7 1 2 3 4 5 6 7
这样经过修改之后特定机型上关于webview的错误就不在了。
webview中实现Native与js相互调用
上面我们介绍的通过cookie实现Android native与H5的信息交互只是一种方式,我们也可以通过java代码与js代码直接相互调用的方式实现Android native与H5信息的相互,这里简单的介绍一下使用方式
native代码调用H5的js代码
(1)在H5页面中添加一个js函数
相关文章推荐
- android产品研发(十八)-->webview问题集锦
- Android产品研发(十八)-->webview问题集锦
- Android产品研发(十八)-->webview问题集锦
- Android WEBVIEW中调用<a href>的问题!!!不是js的方法
- Android开发问题积累 <加载在线Gif><WebView无法加载网页图片>
- Android开发问题积累 <加载在线Gif><WebView无法加载网页图片>
- android中webView焦点以及响应输入发的问题
- android,使用webView加载页面,界面空隙问题
- Android 3.X中WebView使用ZOOM崩溃的问题
- Android 使用Webview无法播放视频问题的解决办法
- 【Android】webView 使用 系统自带搜索对话框问题
- Android WebView点击EditText时整体被拉伸变大问题的解决
- android WebView 控件加载本地sdcard中html文件图片的问题
- android webview 字体切换 反白乱的问题解决
- android开发中webview保存cookie问题的解决
- android 4.0中关于webview加载flash并使flash全屏的问题(附demo)
- Android利用WebView开发browser中flash无法播放的问题
- android webview设置缩放按钮时候无法显示问题
- android中webView焦点以及响应输入发的问题
- android webView 文字、图片分别加载。乱码问题