android webview 63版,Andriod WebView 填坑小结(不定期更新)

[TOC]

在学习WebView的时候就知道了WebView会出现很多稀奇古怪的问题,真碰上的时候还是焦头烂额,很多问题的解决方案要在网上找很久很久很久,只能说MMP。这里做了稍微全面的总结。

划重点:

1.内存泄露的解决方法

2.Native获得的cookie同步到WebView中

3.API5.0以上Ajax跨域访问无法携带cookie的问题

4.Alert劫持问题

1. 内存泄露

关于内存泄漏,想要彻底解决,最好的方法是当你要用webview的时候,另外单独开一个进程(如何单开进程请自行搜索) 去使用webview 并且当这个 进程结束时,请手动调用System.exit(0)。 但是这种情况又会有另外的问题,就是进程间的通信。

不单开进程时:

​ 1.1 JS无法释放,WebView在执行JS时被关闭,这些JS资源会无法释放,一直耗电,一直占CPU,解决方法是在onStop和onResume里分别把setJavaScriptEnabled();给设置成false和true。

@Override

protected void onResume() {

mWebView.getSettings().setJavaScriptEnabled(true);

super.onResume();

}

@Override

protected void onStop() {

mWebView.getSettings().setJavaScriptEnabled(false);

super.onStop();

}

​ 1.2在onDestroy中对webView的销毁做处理,下面是我觉得比较好的方式(主要是处理的比较全面)

if (mWebView != null) {

// 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再

// destory()

ViewParent parent = mWebView.getParent();

if (parent != null) {

((ViewGroup) parent).removeView(mWebView);

}

mWebView.stopLoading();

// 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错

mWebView.getSettings().setJavaScriptEnabled(false);

mWebView.clearHistory();

mWebView.clearAnimation();

mWebView.clearView();

mWebView.removeAllViews();

try {

mWebView.destroy();

} catch (Throwable ex) {

}

2. WebView的各种设置

2.1 首先是创建

最好不要在XML中直接添加WebView,而是预留一个FrameLayout,代码中创建WebView,添加到FrameLayout中。

这样能解决很多内存泄露的问题。

mWebViewContainer = (FrameLayout) findViewById(R.id.web_view_container);

mWebView = new WebView(WebViewActivity.this);

mWebViewContainer.addView(mWebView, new FrameLayout.LayoutParams(

FrameLayout.LayoutParams.MATCH_PARENT,

FrameLayout.LayoutParams.MATCH_PARENT));

2.2 然后是settings,N多种方法

WebSettings webSettings = webView.getSettings();

//设置了这个属性后我们才能在 WebView 里与我们的 Js 代码进行交互,对于 WebApp 是非常重要的,默认是 false,

//因此我们需要设置为 true,这个本身会有漏洞,具体的下面我会讲到

webSettings.setJavaScriptEnabled(true);

//设置 JS 是否可以打开 WebView 新窗口

webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

//WebView 是否支持多窗口,如果设置为 true,需要重写

//WebChromeClient#onCreateWindow(WebView, boolean, boolean, Message) 函数,默认为 false

webSettings.setSupportMultipleWindows(true);

//这个属性用来设置 WebView 是否能够加载图片资源,需要注意的是,这个方法会控制所有图片,包括那些使用 data URI 协议嵌入

//的图片。使用 setBlockNetworkImage(boolean) 方法来控制仅仅加载使用网络 URI 协议的图片。需要提到的一点是如果这

//个设置从 false 变为 true 之后,所有被内容引用的正在显示的 WebView 图片资源都会自动加载,该标识默认值为 true。

webSettings.setLoadsImagesAutomatically(false);

//标识是否加载网络上的图片(使用 http 或者 https 域名的资源),需要注意的是如果 getLoadsImagesAutomatically()

//不返回 true,这个标识将没有作用。这个标识和上面的标识会互相影响。

webSettings.setBlockNetworkImage(true);

//显示WebView提供的缩放控件

webSettings.setDisplayZoomControls(true);

webSettings.setBuiltInZoomControls(true);

//推荐使用。打开 WebView 的 storage 功能,这样 JS 的 localStorage,sessionStorage 对象才可以使用

webSettings.setDomStorageEnabled(true);

//推荐打开。设置是否启动 WebView 的DB API,默认值为 false

webSettings.setDatabaseEnabled(true);

webSettings.setDatabasePath(Utils.getContext().getDir("WebDb",MODE_PRIVATE).getPath());

//打开 WebView 自带的的 LBS 功能,这样 JS 的 geolocation 对象才可以使用,注意manifest中的权限

webSettings.setGeolocationEnabled(true);

webSettings.setGeolocationDatabasePath("");

//设置是否打开 WebView 表单数据的保存功能

webSettings.setSaveFormData(true);

//设置 WebView 的默认 userAgent 字符串

webSettings.setUserAgentString("");

// 不推荐使用。设置缓存,是否开启,缓存模式,缓存大小,缓存路径

webSettings.setAppCacheEnabled(true);

webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);

webSettings.setAppCacheMaxSize(10 * 1024 * 1024);

webSettings.setAppCachePath(Utils.getContext().getExternalCacheDir().getAbsolutePath());

// 这两个一起设置,表明WebView会自适应手机屏幕的宽度

webSettings.setUseWideViewPort(true);

webSettings.setLoadWithOverviewMode(true);

//设置 WebView 的字体,可以通过这个函数,改变 WebView 的字体,默认字体为 "sans-serif"

webSettings.setStandardFontFamily("");

//设置 WebView 字体的大小,默认大小为 16

webSettings.setDefaultFontSize(20);

//设置 WebView 支持的最小字体大小,默认为 8

webSettings.setMinimumFontSize(12);

//设置文本的缩放倍数,默认为 100

webSettings.setTextZoom(2);

// 允许高版本http和https混合访问

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);

}

// 支持PC上的Chrome调试WebView,具体方法请百度

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

// 只支持Debug模式下调试

if (0 != (getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE)) {

WebView.setWebContentsDebuggingEnabled(true);

}

}

2.3 然后是WebViewClient和ChromeClient的设置

WebViewClient主要帮助WebView处理各种通知、请求事件

WebChromeClient主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度等

2.3.1 WebViewClient的常用设置

shouldOverrideUrlLoading方法:在点击请求的是链接时会调用此方法。返回false表明交给系统浏览器处理跳转请求;返回true,表明在WebView里面处理新的跳转请求。一般写法如下。

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

@Override

public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {

view.loadUrl(request.getUrl().toString());

return true;

}

@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

view.loadUrl(url);

return true;

}

2.onReceivedSslError:处理https请求。2.1以上版本,前提setJavaScriptEnabled(true);

@Override

public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {

//handler.cancel(); 默认的处理方式,WebView变成空白页

handler.proceed();//接受证书

//handleMessage(Message msg); 其他处理

}

3.onLoadResource:加载资源时调用,每个资源(比如图片,js,CSS等)都会调用一次。

@Override

public void onLoadResource(WebView view, String url)

4.onPageStarted和onPageStarted:页面加载开始和结束后会调用。

@Override

public void onPageStarted(WebView view, String url, Bitmap favicon)

@Override

public void onPageFinished(WebView view, String url)

5.shouldInterceptRequest:同样是加载资源时调用,但是这里WebView可以加载本地的资源提供给内核,若本地处理返回数据,内核不从网络上获取数据。从API 11时引入, API21更新重载方法。加载本地资源使用方法请看 这篇博客 的 常用资源预加载 。

//API 22添加的重载方法

@Override

public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)

@Override

public WebResourceResponse shouldInterceptRequest(WebView view, String url)

因为上面的博客被删除了,在这里写一下如何加载本地资源,包括Js文件,图片文件等等

这里举例:需要加载本地的Js文件jsTest.js和icon.png。注意,这里需要把文件放在src/main/assests下

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

@Override

public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

String url = request.getUrl().toString();

if (!TextUtils.isEmpty(url) && url.contains("jsTest.js")) {

return editResponse("jsTest.js");

} else if (!TextUtils.isEmpty(url) && url.contains("icon.png")) {

return editResponse("icon.png");

}

return super.shouldInterceptRequest(view, request);

}

@Override

public WebResourceResponse shouldInterceptRequest(WebView view, String url) {

// 因为加载每个图片,Js资源都要请求一次网络。所以这种判断这种资源是否在本地,如果在本地,就直接返回本地的资源,不再去网上取

if (url.contains("jsTest.js")) {

return editResponse("jsTest.js");

} else if (url.contains("icon.png")) {

return editResponse("icon.png");

}

return super.shouldInterceptRequest(view, url);

}

/**

* 自定义的得到src/main/assests下资源文件的方法

*/

private WebResourceResponse editResponse(String fileName) {

try {

return new WebResourceResponse("application/x-javascript", "utf-8", getAssets().open(fileName));

} catch (IOException e) {

e.printStackTrace();

}

//需处理特殊情况

return null;

}

2.3.2 ChromeClient的常用设置

1.onJsAlert/onJsConfirm/onJsPrompt方法,对应JS的对话框,确认框,输入框。JS弹出对话框等时,会调用对应的方法,我们在方法中弹出AlertDialog等,显示相应信息即可

2.onProgressChanged:通知应用程序当前网页加载的进度

@Override

public void onProgressChanged(WebView view, int newProgress)

3.onReceivedTitle:获取网页title标题。

获取标题的时间主要取决于网页前段设置标题的位置,一般设置在页面加载前面,可以较早调用到这个函数

@Override

public void onReceivedTitle(WebView view, String title)

4.H5播放器全屏和去全屏方法

//有H5视频,按下全屏播放时调用的方法

@Override

public void onShowCustomView(View view, CustomViewCallback callback)

// 对应的取消全屏方法

@Override

public void onHideCustomView()

5.设置WebView视频未播放时默认显示占位图。关于WebView中的视频播放的相关知识,请点这里

@Override

public Bitmap getDefaultVideoPoster()

3. Native与JS的相互调用

这里只说重点:

Js调用Android现在一般是用WebView的addJavascriptInterface()方式,当然因为Android版本造成的漏洞不能忽视

Android调用Js,毫无疑问,evaluateJavascript()和loadUrl结合使用,根据版本判断

常见错误:

1.线程错误。Js调Android时发生,Android调时也经常发生,因为调了Js,很多情况还是会回调Android。报错信息大致为Js线程必须一致等。很好解决,Android中在UI线程即可。但是推荐WebView.post()的方式,不推荐runOnUiThread()方式

mWebView.post(new Runnable() {

@Override

public void run() {

// TODO

}

});

4. Cookie问题

4.1 WebView是有自己的Cookie系统的

WebView会在本地维护每次会话的cookie(保存在data/data/package_name/app_WebView/Cookies.db)。当WebView加载URL的时候,WebView会从本地读取该URL对应的cookie,并携带该cookie与服务器进行通信。

WebView通过android.webkit.CookieManager类来维护cookie。CookieManager是WebView的cookie管理类。

4.2 Native获得Cookie,设置给WebView如何做。

很多时候我们需要将在native中的 登陆的状态 同步到H5中避免再次登陆,这就需要用到对Cookie的管理。

问题分解:

1.在OkHttp(假定使用的是OkHttp,其他的方式获得cookie更简单)中获得cookie,并储存;

2.取得cookie并设置给WebView。

解决:

4.2.1 Okhttp中获得cookie。

获得cookie很简单,只需在OkHttpClient的构建过程中加一行代码。

List mCookies;

mOkHttpClient = new OkHttpClient.Builder()

...

// 下面就是对OkHttp的cookie的处理

.cookieJar(new CookieJar() {

// 对服务器返回的cookie的处理的方法

// 参数url和cookie就是cookie对应的url和cookie的值

@Override

public void saveFromResponse(HttpUrl url, List cookies) {

// 对cookie的处理,一般是存到内存中,使其他地方可以获得

mCookies = cookies;

}

// 发送请求时的cookie的处理,返回的List即请求的cookie

@Override

public List loadForRequest(HttpUrl url) {

return mCookies;

//return null;

}

})

.build();

当然,也可以新建一个类,实现CookieJar,专门处理cookie问题。这里只是给出了最简单的对cookie的处理,对于持久化等,就是另外的问题了。

4.2.2 将cookie同步到WebView中

private void syncCookies(String url, List cookies) {

// 一些前提设置

CookieSyncManager.createInstance(this);

final CookieManager cookieManager = CookieManager.getInstance();

cookieManager.setAcceptCookie(true);

/**

* 设置webView支持JS的Cookie的调用,5.0以上才要设置

*/

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

cookieManager.setAcceptThirdPartyCookies(mWebView, true);

}

//cookieManager.removeAllCookie();

// 向WebView中添加Cookie,

for (Cookie cookie : cookies) {

cookieManager.setCookie(url, cookie.toString());

}

// 刷新,同步

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {

CookieManager.getInstance().flush();

}

CookieSyncManager.getInstance().sync();

//String newCookie = cookieManager.getCookie(url);验证是否将cookie同步进去

//KLog.i("newCookie: " + newCookie);

}

这里有几点要注意:

1.顺序。

// 1.先初始化WebView,各种设置setting,webViewClient和chromeClient等

initWebView();

// 2.再获得,并同步cookie

// MyCookieJar.getInstance().getCookies()可以换成ApiManager.getInstance().getCookies()等

syncCookies(url, MyCookieJar.getInstance().getCookies());

// 3.最后加载url

mWebView.loadUrl(url);

2.cookie的添加时,最好是一个cookie,set一次,最好不要自己拼接,否则关于domain,path,逗号,分号等等的问题会让人欲仙欲死。还有说法是用String不行,要用StringBuilder。所以,尽量不自己拼。

4.3 Ajax跨域访问时,Cookie带不过去的解决方法

问题:Native已经登录,cookie可以设置进去,但是网页进行了复杂的ajax操作(我也不知道什么操作),cookie带不过去,到指定页面还得登录。5.0以下正常

解决:经过排查,发现高版本时问题出在ajax跳转时,是Js对Cookie的操作,不经过WebView,正常的WebView设置没有作用。看了很多博客,还是在StackOverFlow上发现解决方法。一行代码

final CookieManager cookieManager = CookieManager.getInstance();

cookieManager.setAcceptCookie(true);

/**

* 设置webView支持JS的Cookie的调用,5.0以上才要设置

*/

// 大致意思:mWebView接收第三方对Cookie的操作,也就是支持Js对cookie的操作

cookieManager.setAcceptThirdPartyCookies(mWebView, true);

5. 棘手问题

5.1 Alert劫持

Alert劫持:Alert只会弹出一次,并且WebView会卡死。重新加载都不行,必须杀死进程,重新打开App

解决方法很简单,在自定义的onJsAlert方法中加一行代码

@Override

public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

...

result.confirm();// 不加这行代码,会造成Alert劫持:Alert只会弹出一次,并且WebView会卡死

return true;

}

6. 其他

6.1 带进度条的WebView

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值