前言
现在越来越多的APP都内置了Web网页去加载视图,也就是我们常说的Hybrid APP混合应用,市场上很多平台都是这样做的,比如我们经常使用的淘宝、京东等电商平台。这些是如何实现的呢?在我们Android中有一个WebView组件,它就可以实现此类功能。它是Android中的原生UI控件,主要用于在APP应用中方便地访问远程网页或本地HTML资源,同时WebView也在Android中充当Java代码和JS代码之间交互的桥梁,实际上也可以将WebView看做一个功能最小化的浏览器。下面我们一起来看看WebView的一些常用的使用方法。
一、简介
WebView是Android系统提供的一个能显示网页的系统控件,它是一个特殊的View,同时也是一个ViewGroup,可以有很多其他子View。在Android 4.4以下(不包含4.4)系统WebView底层实现是采用WebKit内核,而在Android 4.4及其以上Google采用了Chromium内核作为系统WebView的底层内核支持。在这一变化中Android提供的WebView相关API并没有发生较大变化,在4.4上也兼容低版本的API并且引进了少部分API。这里简单介绍下基于Chromium的WebView和基于WebKit的WebView之间的差异,基于Chromium的Webview提供了更广的HTML5、CSS3、JavaScript支持,在Android系统版本5.0上基于Chromium 37,WebView提供了绝大多数的HTML5特性支持,除此之外Chromium也支持远程调试(Chrome DevTools)。WebKit JavaScript引起采用WebCore JavaScript在Android 4.4上换成了V8能直接提升JavaScript性能。
二、作用
1、显示和渲染Web页面
2、使用html文件(网络上或本地assets中)作为布局
3、可与JavaScript交互调用
注: WebView控件功能强大,除了具有一般View的属性和设置外,还可以对Url请求、页面加载、渲染、页面交互进行强大的处理。
三. 使用
3.1 配置网络权限
<uses-permission android:name="android.permission.INTERNET" />
3.2 Webview常用方法
3.2.1 WebView的状态
// 激活WebView为活跃状态,能正常执行网页的响应
webView.onResume() ;
// 当页面失去焦点或者切换到后台不可见状态时需要执行onPause方法
// 通过onPause方法通知内核暂停所有的动作,比如DOM解析、Plugin执行、JavaScript执行等
webView.onPause();
// 当应用程序(存在WebView)切换到后台时此方法不仅仅针对当前的WebView,而是全局、全应用程序的WebView
// 它会暂停所有WebView的layout、parsing、javascripttimer等,降低CPU功耗
webView.pauseTimers()
// 恢复pauseTimers状态
webView.resumeTimers();
// 停止当前加载
webView.stopLoading();
// 销毁WebView,当关闭了当前Activity时,如果WebView中的音乐或视频还在播放,此时就必须销毁当前的WebView
// 需要注意的是:WebView调用destory方法时,WebView仍然绑定在Activity上,这是由于自定义WebView构建时传入了该Activity的context对象
// 因此需要在销毁时先从父容器中移除当前的WebView,然后再销毁WebView
rootLayout.removeView(webView);
// 清空子View
webView.removeAllViews();
webView.destroy();
3.2.2 网页加载上一页和下一页
// 是否可以跳到上一页(如果返回false,说明已经是第一页)
Webview.canGoBack()
// 跳到上个页面
Webview.goBack()
// 是否可以跳到下一页(如果返回false,说明已经是最后一页)
Webview.canGoForward()
// 跳到下个页面
Webview.goForward()
// 以当前的index为起始点前进或者后退到历史记录中指定的steps,如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps)
例如:使用Back按键控制网页后退
问题:不做任何处理的前提下浏览网页时点击系统的Back按键,整个Browser浏览器会调用finish()方法而结束自身
目标:点击返回按键后只是网页回退而不是退出整个浏览器
方案:在当前Activity中处理并消费掉该Back事件
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == event.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
3.2.3 清除缓存数据
// 清除网页访问后留下的缓存,由于内核缓存是全局的,因此这个方法不仅仅针对WebView,而是针对整个应用程序
Webview.clearCache(true);
// 清除当前WebView访问的历史记录,只会将WebView访问的历史记录里中的所有记录清除,除了当前访问记录
Webview.clearHistory();
// 仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
// 向Web端注入Java对象
mWebView.removeJavascriptInterface("AndroidNative");
}
// 清除网页查找的高亮匹配字符
Webview.clearMatches();
// 清除ssl信息
Webview.clearSslPreferences();
// 删除缓存数据库
context.deleteDatabase("webview.db");
context.deleteDatabase("webviewCache.db");
// 删除缓存文件夹
File file = CacheManager.getCacheFileBaseDir();
if (file != null && file.exists() && file.isDirectory()) {
for (File item : file.listFiles()) {
item.delete();
}
file.delete();
}
3.2.4 Cookie设置
// Cookie设置
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
String cookie = "name=xxx;age=18";
cookieManager.setCookie(URL, cookie);
CookieSyncManager.getInstance().sync();
// 获取Cookie
CookieManager cookieManager = CookieManager.getInstance();
String cookie = cookieManager.getCookie(URL);
// 清除Cookie
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
CookieSyncManager.getInstance().sync();
3.3 常用类使用
3.3.1 WebSettings类的使用
注:对WebView进行配置和管理。
生成WebView组件(有两种方式):
// 方式1:直接在Activity中通过new关键字生成
WebView webView = new WebView(this);
// 方法2:在Activity的layout布局文件里添加WebView组件
WebView webView = (WebView) findViewById(R.id.webView);
使用WebSettings对WebView进行配置
// 声明WebSettings子类
WebSettings webSettings = webView.getSettings();
// 如果访问的页面中需要与JavaScript进行交互,则WebView必须设置支持JavaScript
webSettings.setJavaScriptEnabled(true);
// 设置字体缩放倍数,默认100
webSettings.setTextZoom(100);
// 支持插件
webSettings.setPluginsEnabled(true);
// 设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); // 支持双击缩放(wap网页不支持)
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕大小
// 缩放操作
webSettings.setSupportZoom(true); // 支持缩放,默认为true,是下面那个的前提
webSettings.setBuiltInZoomControls(true); // 显示缩放按钮(wap网页不支持),若为false则该WebView不可缩放
webSettings.setDisplayZoomControls(false); // 隐藏原生的缩放控件
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
// 其他细节操作
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); // 支持内容重新布局
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // 关闭WebView中缓存
webSettings.setAllowFileAccess(true); // 设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 支持通过js打开新窗口
webSettings.setLoadsImagesAutomatically(true); // 支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8"); // 设置编码格式
webSettings.supportMultipleWindows(); // 多窗口
webSettings.setNeedInitialFocus(true); // 当WebView调用requestFocus时为WebView设置节点
// 开启DOM缓存
webSettings.setDomStorageEnabled(true);
// 开启数据库缓存
webSettings.setDatabaseEnabled(true);
// 支持启用缓存模式
webSettings.setAppCacheEnabled(true);
// 设置AppCache最大缓存值(现在官方已经不提倡使用,已废弃)
webSettings.setAppCacheMaxSize(8 * 1024 * 1024);
// Android私有缓存存储,如果你不调用setAppCachePath方法,WebView将不会产生这个目录
webSettings.setAppCachePath(getCacheDir().getAbsolutePath());
// 数据库路径
if (!hasKitkat()) {
webSettings.setDatabasePath(getDatabasePath("html").getPath());
}
// 关闭密码保存提醒功能
webSettings.setSavePassword(false);
// 设置UserAgent属性
webSettings.setUserAgentString("");
// 允许通过file url加载的JavaScript读取其他的本地文件,Android 4.1之前默认是true,在 Android 4.1及以后默认是false,也就是禁止
webSettings.setAllowFileAccessFromFileURLs(false);
// 允许通过file url加载的JavaScript可以访问其他的源,包括其他的文件和http、https等其他的源
// Android 4.1之前默认是true,在 Android 4.1及以后默认是false,也就是禁止
// 如果此设置是允许,则setAllowFileAccessFromFileURLs不起做用
webSettings.setAllowUniversalAccessFromFileURLs(false);
常见用法:设置WebView缓存
当加载html页面时WebView会在/data/data/包名目录下生成database与cache两个文件夹
请求的Url记录保存在WebViewCache.db,而Url的内容是保存在WebViewCache文件夹下
// 缓存模式有如下几种
// LOAD_CACHE_ONLY:不使用网络,只读取本地缓存数据
// LOAD_DEFAULT:(默认)根据cache-control决定是否从网络上读取数据
// LOAD_NO_CACHE:不使用缓存,只从网络上获取数据
// LOAD_CACHE_ELSE_NETWORK:只要本地有缓存,无论是否过期或者no-cache都使用缓存中的数据
// 优先使用缓存
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// 不使用缓存
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
多种方式结合使用(离线加载)
if (NetWorkUtils.isConnected(getApplicationContext())) {
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 根据cache-control来决定是否从网络上读取数据
} else {
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // 无网络时从本地获取数据即离线加载
}
webSettings.setDomStorageEnabled(true); // 开启DOM storage API功能
webSettings.setDatabaseEnabled(true); // 开启database storage API功能
webSettings.setAppCacheEnabled(true); // 开启Application Caches功能
String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath); // 设置Application Caches缓存目录
注:每个Application只调用一次WebSettings.setAppCachePath()和WebSettings.setAppCacheMaxSize()
3.3.2 WebViewClient类的使用
注:处理各类通知和请求事件。
loadUrl方法:
// 定义WebView组件
WebView webView = (WebView) findViewById(R.id.webView);
// 方式1:加载网页
webView.loadUrl("http://www.google.com/");
// 方式2:加载本地assets目录下的网页
webView.loadUrl("file:///android_asset/test.html");
// 方式3:加载手机本地的html页面
webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
shouldOverrideUrlLoading方法:
// 复写shouldOverrideUrlLoading()方法,使打开网页时不调用系统浏览器而是在本WebView中显示
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
其他方法:
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
// 作用:开始载入页面时调用,我们可以设定一个loading动画,告知用户程序在等待网络响应。
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
// 作用:在页面加载结束时调用,我们可以关闭loading加载动画,切换程序动作。
}
@Override
public void onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
// 作用:在加载页面资源时调用,每个资源(如图片)的加载都会调用一次。
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
// 作用:加载页面的服务器出现错误时(如404)调用。
// App中使用WebView控件的时候遇到诸如404这类错误的时候,若显示浏览器里面的默认错误提示页就显得很难看了,
// 那么此时我们的App就可以加载一个本地的错误提示页面即WebView如何加载一个本地的页面
// 步骤1:定义一个html文件(error_handle.html)用于在出错时展示给用户看
// 步骤2:将该html文件放置到代码根目录的assets文件夹下
// 步骤3:复写WebViewClient的onReceivedError方法
// 该方法回传了错误码,可根据错误类型进行不同的错误分类处理
switch (errorCode) {
case HttpStatus.SC_NOT_FOUND:
view.loadUrl("file:///android_assets/error_handle.html");
break;
}
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
handler.proceed(); // 表示等待证书响应
// handler.cancel(); // 表示挂起连接,为默认方式
// handler.handleMessage(null);// 可做其他处理
}
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
// 当WebView得页面Scale值发生改变时回调
}
/**
* WebView可以拦截某一次的request来返回我们自己加载的数据,这个方法在后面缓存会有很大作用。
*
* @param view WebView
* @param request 当前产生 request 请求
* @return WebResourceResponse
*/
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return super.shouldInterceptRequest(view, request);
}
});
3.3.3 WebChromeClient类的使用
注:辅助WebView处理JavaScript的对话框、网站图标、网站标题等等。
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
// 作用:获得网页的加载进度并显示
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
// 作用:获取Web网页中的标题
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
// 接收web页面的icon
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
// Js中调用alert()函数产生的对话框
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
// 处理Js中的Confirm对话框
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 处理Js中的Prompt对话框
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
3.4 如何避免WebView内存泄露
3.4.1 不在xml中定义Webview,而是在Activity中进行创建且Context使用getApplicationContext()
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
WebView mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
3.4.2 在Activity销毁的时候,先将WebView加载null内容,然后移除WebView,再销毁WebView,最后置空
@Override
protected void onDestroy() {
if (mWebView != null) {
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
四、与JavaScript交互
1、使用系统方法addJavascriptInterface注入Java对象来实现。
2、利用WebViewClient中shouldOverrideUrlLoading(WebView view, String url)接口来拦截操作。这个scheme是通过约定Url协议后双方各自解析,针对相应的Url使用iframe机制来调用对应的native代码来实现互通。
3、利用WebChromeClient中的onJsAlert、onJsConfirm、onJsPrompt提示接口来实现,其实也是属于拦截操作。
// 开启Js可用
mWebView.getSettings().setJavaScriptEnabled(true);
// WebView注入即可
mWebView.addJavascriptInterface(new NativeInterface(this), "AndroidNative");
// 创建要注入的Java类
public class NativeInterface {
private Context mContext;
public NativeInterface(Context context) {
mContext = context;
}
@JavascriptInterface
public void hello() {
Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void hello(String params) {
Toast.makeText(mContext, params, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public String getAndroid() {
Toast.makeText(mContext, "getAndroid", Toast.LENGTH_SHORT).show();
return "Android Data";
}
}
// Js编写
<script>
function callHello() {
AndroidNative.hello();
}
function callHello1() {
AndroidNative.hello('Hello Android');
}
function callAndroid() {
var temp = AndroidNative.getAndroid();
console.log(temp);
alert(temp);
}
</script>
Native调用Js:mWebView.loadUrl(js);
Js调用Native :AndroidNative.getAndroid();
注: Android 4.2版本以下会存在漏洞,4.2以上需要添加@JavascriptInterface注解才能被调用到,Js调用方式不变。
五、Js注入漏洞
虽然可以通过注入方式来实现WebView和JS交互,但是实现功能的同时也带了安全问题,通过注入的Java类作为桥梁,JS 就可以利用这个漏洞来进行攻击操作。
常见漏洞:
目前已知的WebView漏洞有4个,它们分别是:
1、使用WebView中addJavascriptInterface接口会引起远程代码执行漏洞。
2、针对某些特定机型会存在addJavascriptInterface API引起的远程代码执行漏洞。
3、WebView中内置导出的“searchBoxJavaBridge_”Java Object接口可能被利用实现远程任意代码执行。
4、WebView中内置导出的“accessibility”和“accessibilityTraversal”两个Java Object接口可能被利用实现远程任意代码执行。
解决漏洞
1、在Android 4.2以下不在使用addJavascriptInterface方式,Android 4.2以上需要添加注解@JavascriptInterface才能调用。
2、解决方式如方式1即可。
3、创建WebView时使用removeJavascriptInterface方法将系统注入的”searchBoxJavaBridge_”对象删除。
4、当系统辅助功能服务被开启时,在Android 4.4以下系统中由系统提供的WebView组件都默认导出”accessibility”和”accessibilityTraversal”两个接口,这两个接口同样存在被远程任意代码执行的威胁,同样需要通过removeJavascriptInterface方法将这两个对象删除。
super.removeJavascriptInterface("searchBoxJavaBridge_");
super.removeJavascriptInterface("accessibility");
super.removeJavascriptInterface("accessibilityTraversal");
以上都是系统机制层面上出现的一些漏洞,还有一些是使用WebView时产生的漏洞,下面我们一起来看一下。
5、通过webSettings.setSavePassword(false)关闭密码保存提醒功能,防止明文密码存在本地被盗用。
6、WebView默认是可以使用File协议的,也就是设置setAllowFileAccess(true)方法,我们应主动将其设置为setAllowFileAccess(false)防止加载本地文件,移动版的Chrome默认禁止加载File协议的文件。
webSettings.setAllowFileAccess(true); // 设置为false后将不能加载本地html文件
webSettings.setAllowFileAccessFromFileURLs(false);
webSettings.setAllowUniversalAccessFromFileURLs(false);
if (url.startsWith("file://")) {
webSettings.setJavaScriptEnabled(false);
} else {
webSettings.setJavaScriptEnabled(true);
}
修复案例
SafeWebView这个库解决了Android WebView中Js注入安全漏洞以及其它的一些异常问题。
解决问题如下:
1、WebView addJavascriptInterface安全漏洞问题;
2、支持网页将Js函数(function)传到Java层方便回调;
3、解决各种WebView的崩溃问题(附日志);
4、WebView设置代理(对不同Android系统版本调用Java反射实现);
第三方库解决
六、常见问题
由于Android版本碎片化严重,在使用WebView的时候会遇到各种问题,特别是在Android 4.4之后更换了WebView内核,而且在4.2以下版本有部分漏洞,下面整理了一些常见的问题及解决方法,仅供参考。
1、android.webkit.AccessibilityInjector$TextToSpeechWrapper
java.lang.NullPointerException
at android.webkit.AccessibilityInjector$TextToSpeechWrapper$1.onInit(AccessibilityInjector.java:753)
at android.speech.tts.TextToSpeech.dispatchOnInit(TextToSpeech.java:640)
at android.speech.tts.TextToSpeech.initTts(TextToSpeech.java:619)
at android.speech.tts.TextToSpeech.<init>(TextToSpeech.java:553)
at android.webkit.AccessibilityInjector$TextToSpeechWrapper.<init>(AccessibilityInjector.java:676)
at android.webkit.AccessibilityInjector.addTtsApis(AccessibilityInjector.java:480)
at android.webkit.AccessibilityInjector.addAccessibilityApisIfNecessary(AccessibilityInjector.java:168)
at android.webkit.AccessibilityInjector.onPageStarted(AccessibilityInjector.java:340)
at android.webkit.WebViewClassic.onPageStarted(WebViewClassic.java:4480)
at android.webkit.CallbackProxy.handleMessage(CallbackProxy.java:366)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5407)
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:833)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
这个问题在Android 4.2.1和4.2.2版本上比较集中,可采用关闭辅助功能来解决。
解决方法:在初始化WebView的时候调用disableAccessibility方法即可。
public static void disableAccessibility(Context context) {
// Android 4.2 Build.VERSION_CODES.JELLY_BEAN_MR1
if (Build.VERSION.SDK_INT == 17) {
if (context != null) {
try {
AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
if (!am.isEnabled()) {
//Not need to disable accessibility
return;
}
Method setState = am.getClass().getDeclaredMethod("setState", int.class);
setState.setAccessible(true);
/**{@link AccessibilityManager#STATE_FLAG_ACCESSIBILITY_ENABLED}*/
setState.invoke(am, 0);
} catch (Exception ignored) {
ignored.printStackTrace();
}
}
}
}
2、android.content.pm.PackageManager$NameNotFoundException
AndroidRuntimeException: android.content.pm.PackageManager$NameNotFoundException: com.google.android.webview
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4604)
at android.app.ActivityThread.access$1500(ActivityThread.java:154)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1389)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5302)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:916)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:711)
Caused by: android.util.AndroidRuntimeException: android.content.pm.PackageManager$NameNotFoundException: com.google.android.webview
at android.webkit.WebViewFactory.getFactoryClass(WebViewFactory.java:174)
at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:109)
at android.webkit.WebView.getFactory(WebView.java:2194)
at android.webkit.WebView.ensureProviderCreated(WebView.java:2189)
at android.webkit.WebView.setOverScrollMode(WebView.java:2248)
at android.view.View.<init>(View.java:3588)
at android.view.View.<init>(View.java:3682)
at android.view.ViewGroup.<init>(ViewGroup.java:497)
at android.widget.AbsoluteLayout.<init>(AbsoluteLayout.java:55)
at android.webkit.WebView.<init>(WebView.java:544)
at android.webkit.WebView.<init>(WebView.java:489)
at android.webkit.WebView.<init>(WebView.java:472)
at android.webkit.WebView.<init>(WebView.java:459)
at android.webkit.WebView.<init>(WebView.java:449)
在创建WebView的时候崩溃,跟进栈信息,提示我们需要在setOverScrollMode方法上添加异常保护处理。
解决方法如下:
try {
super.setOverScrollMode(mode);
} catch (Throwable e) {
e.printStackTrace();
}
上面方式捕获异常的范围有点广,下面提供一个有针对性的修复方法。
try {
super.setOverScrollMode(mode);
} catch (Throwable e) {
String messageCause = e.getCause() == null ? e.toString() : e.getCause().toString();
String trace = Log.getStackTraceString(e);
if (trace.contains("android.content.pm.PackageManager$NameNotFoundException")
|| trace.contains("java.lang.RuntimeException: Cannot load WebView")
|| trace.contains("android.webkit.WebViewFactory$MissingWebViewPackageException: Failed to load WebView provider: No WebView installed")) {
e.printStackTrace();
} else {
throw e;
}
}
3、android.webkit.WebViewClassic.clearView
at android.webkit.BrowserFrame.nativeLoadUrl(Native Method)
System.err: at android.webkit.BrowserFrame.loadUrl(BrowserFrame.java:279)
System.err: at android.webkit.WebViewCore.loadUrl(WebViewCore.java:2011)
System.err: at android.webkit.WebViewCore.access$1900(WebViewCore.java:57)
System.err: at android.webkit.WebViewCore$EventHub$1.handleMessage(WebViewCore.java:1303)
System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
System.err: at android.os.Looper.loop(Looper.java:137)
System.err: at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:812)
System.err: at java.lang.Thread.run(Thread.java:856)
webcoreglue: *** Uncaught exception returned from Java call!
System.err: java.lang.NullPointerException
System.err: at android.webkit.WebViewClassic.clearView(WebViewClassic.java:2868)
System.err: at android.webkit.WebViewCore.setupViewport(WebViewCore.java:2497)
System.err: at android.webkit.WebViewCore.updateViewport(WebViewCore.java:2479)
System.err: at android.webkit.BrowserFrame.nativeLoadUrl(Native Method)
System.err: at android.webkit.BrowserFrame.loadUrl(BrowserFrame.java:279)
System.err: at android.webkit.WebViewCore.loadUrl(WebViewCore.java:2011)
System.err: at android.webkit.WebViewCore.access$1900(WebViewCore.java:57)
System.err: at android.webkit.WebViewCore$EventHub$1.handleMessage(WebViewCore.java:1303)
System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
System.err: at android.os.Looper.loop(Looper.java:137)
这个异常是在某些设备上发生的,在调用webView.destroy()之前调用了loadUrl操作发生的,在清空webView destroy时调用清理方法,内部可能时机有问题会出现,WebViewClassic中mWebViewCore对象为null,其内部为Handler消息机制实现。
解决方法如下:
public void logdUrl(final String url) {
try {
super.loadUrl(url);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
项目地址 ☞ 传送门
本文详细介绍了Android中的WebView组件,包括其作用、使用方法、与JavaScript的交互、Js注入漏洞及其解决方案,并探讨了常见问题和最佳实践,旨在帮助开发者更好地理解和安全使用WebView。
1244

被折叠的 条评论
为什么被折叠?



