????????Android?WebView
????????一、簡介
????????WebView在Android平臺上是一個特殊的View, 基于webkit引擎、展現web頁面的控件,這個類可以被用來在你的app中僅僅顯示一張在線的網頁,還可以用來開發瀏覽器。WebView內部實現是采用渲染引擎來展示view的內容,提供網頁前進后退,網頁放大,縮小,搜索。Android的Webview在低版本和高版本采用了不同的webkit版本內核,4.4后直接使用了Chrome內核。
????????現在很多APP都內置了Web網頁,比如說很多電商平臺,淘寶、京東、聚劃算等等。WebView比較靈活,不需要升級客戶端,只需要修改網頁代碼即可。一些經常變化的頁面可以用WebView這種方式去加載網頁。例如中秋節跟國慶節打開的頁面不一樣,如果是用WebView顯示的話,只修改修改html頁面就行,而不需要升級客戶端。
????????二、基本使用
????????1、添加網絡權限
<uses-permission android:name="android.permission.INTERNET"/>
????????2、添加布局
<WebView
????android:id="@+id/wv_webview"
????android:layout_width="match_parent"
????android:layout_height="match_parent"/>
????????3、基本代碼運用
webView = (WebView) findViewById(R.id.wv_webview);
// webView.loadUrl("file:///android_asset/test.html");//加載asset文件夾下html
webView.loadUrl("http://139.196.35.30:8080/OkHttpTest/apppackage/test.html");//加載url
webView.addJavascriptInterface(this,"android");//添加js監聽 這樣html就能調用客戶端
webView.setWebChromeClient(webChromeClient);//輔助WebView處理Javascript的對話框,網站圖標,網站title,加載進度條webView.setWebViewClient(webViewClient); //幫助WebView處理各種通知、請求事件
WebSettings webSettings=webView.getSettings();//對WebView進行配置和管理
webSettings.setJavaScriptEnabled(true);//允許使用js
LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據
LOAD_DEFAULT: (默認)根據cache-control決定是否從網絡上取數據。
LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據. ?
LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據。
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);//不使用緩存,只從網絡獲取數據.
//支持屏幕縮放
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
????????三、常用方法
????????1、WebView常用方法
wvWebview.loadUrl(String url);// 加載網絡鏈接 url
wvWebview.canGoBack();// 判斷 WebView 當前是否可以返回上一頁
wvWebview.goBack();// 回退到上一頁
wvWebview.onResume();// 在調用onPause()后,可以調用該方法來恢復WebView 的運行
wvWebview.resumeTimers();// 恢復pauseTimers時的所有操作
wvWebview.onPause();// 類似 Activity 生命周期,頁面進入后臺不可見狀態
wvWebview.pauseTimers();// 該方法面向全局整個應用程序的webview,它會暫停所有webview的layout,parsing,JavaScript Timer。當程序進入后臺時,該方法的調用可以降低CPU功耗。
wvWebview.destroy();// 銷毀 WebView
wvWebview.clearHistory();// 清空歷史
wvWebview.clearCache(true);// 包括硬盤
wvWebview.reload();// 重新加載當前請求
wvWebview.removeAllViews();// 清除子view。
wvWebview.clearSslPreferences();// 清除ssl信息。
wvWebview.clearMatches();// 清除網頁查找的高亮匹配字符。
wvWebview.removeJavascriptInterface(String interfaceName);// 刪除interfaceName對應的注入對象
wvWebview.addJavascriptInterface(Object object,String interfaceName);//注入 java 對象。
wvWebview.setVerticalScrollBarEnabled(true);// 設置垂直方向滾動條。
wvWebview.setHorizontalScrollBarEnabled(true);// 設置橫向滾動條。
wvWebview.loadUrl(String url, Map additionalHttpHeaders);// 加載制定url并攜帶http header數據。。
wvWebview.stopLoading();// 停止 WebView 當前加載。
wvWebview.freeMemory();// 釋放內存,不過貌似不好用。
wvWebview.clearFormData();// 清除自動完成填充的表單數據。需要注意的是,該方法僅僅清除當前表單域自動完成填充的表單數據,并不會清除WebView存儲到本地的數據。
????????2、WebSettings(對WebView進行配置和管理) 常用方法
finalString filesDir = getContext().getFilesDir().getPath();
finalString databaseDir = filesDir.substring(0, filesDir.lastIndexOf("/")) + DATABASES_SUB_FOLDER;
WebSettings webSettings = wvWebview.getSettings();
if(webSettings ==null)return;
webSettings.setJavaScriptEnabled(true);// 支持 Js 使用
webSettings.setDomStorageEnabled(true);// 開啟DOM緩存
webSettings.setDatabaseEnabled(true);// 開啟數據庫緩存
webSettings.setLoadsImagesAutomatically(true);// 支持自動加載圖片
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);// 設置 WebView 的緩存模式
webSettings.setAppCacheEnabled(true);// 支持啟用緩存模式
// Android 私有緩存存儲,如果你不調用setAppCachePath方法,WebView將不會產生這個目錄
webSettings.setAppCachePath(getCacheDir().getAbsolutePath());
if(Build.VERSION.SDK_INT <19) {// 數據庫路徑
? ? webSettings.setDatabasePath(databaseDir);
}
webSettings.setSupportZoom(true);// 支持縮放
webSettings.setUserAgentString("");// 設置 UserAgent 屬性
webSettings.setAllowFileAccess(true);// 允許加載本地 html 文件/false
// 允許通過 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);
webSettings.setDefaultTextEncodingName("utf-8");//設置編碼格式
????????3、WebChromeClient(輔助WebView處理Javascript的對話框,網站圖標,網站title,加載進度條)常用方法
wvWebview.setWebChromeClient(newMyWebChromeClient());
publicclassMyWebChromeClientextendsWebChromeClient{
/**輸出 Web 端日志 */
@Override
publicbooleanonConsoleMessage(ConsoleMessage consoleMessage){
returnsuper.onConsoleMessage(consoleMessage);
? ? ? ? }
/** * 當前 WebView 加載網頁進度 */
@Override
publicvoidonProgressChanged(WebView view,intnewProgress){
super.onProgressChanged(view, newProgress);
? ? ? ? }
/*** Js 中調用 alert() 函數,產生的對話框*/
@Override
publicbooleanonJsAlert(WebView view, String url, String message, JsResult result){
returnsuper.onJsAlert(view, url, message, result);
? ? ? ? }
/** * 處理 Js 中的 Confirm 對話框*/
@Override
publicbooleanonJsConfirm(WebView view, String url, String message, JsResult result){
returnsuper.onJsConfirm(view, url, message, result);
? ? ? ? }
/** * 處理 JS 中的 Prompt對話框 */
@Override
publicbooleanonJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result){
returnsuper.onJsPrompt(view, url, message, defaultValue, result);
? ? ? ? }
/*** 接收web頁面的icon*/
@Override
publicvoidonReceivedIcon(WebView view, Bitmap icon){
super.onReceivedIcon(view, icon);
? ? ? ? }
/** * 接收web頁面的 Title */
@Override
publicvoidonReceivedTitle(WebView view, String title){
super.onReceivedTitle(view, title);
? ? ? ? }
? ? }
????????4、WebViewClient(幫助WebView處理各種通知、請求事件)常用方法
wvWebview.setWebViewClient(newMyWebViewClient());
publicclassMyWebViewClientextendsWebViewClient{
/*** 當WebView得頁面Scale值發生改變時回調 */
@Override
publicvoidonScaleChanged(WebView view,floatoldScale,floatnewScale){
super.onScaleChanged(view, oldScale, newScale);
? ? ? ? }
/** * 是否在 WebView 內加載頁面 */
@Override
publicbooleanshouldOverrideUrlLoading(WebView view, String url){
? ? ? ? ? ? view.loadUrl(url);
returnsuper.shouldOverrideUrlLoading(view, url);
? ? ? ? }
/** * WebView 開始加載頁面時回調,一次Frame加載對應一次回調 */
@Override
publicvoidonPageStarted(WebView view, String url, Bitmap favicon){
super.onPageStarted(view, url, favicon);
? ? ? ? }
/** * WebView 完成加載頁面時回調,一次Frame加載對應一次回調 */
@Override
publicvoidonPageFinished(WebView view, String url){
super.onPageFinished(view, url);
? ? ? ? }
/*** WebView 加載頁面資源時會回調,每一個資源產生的一次網絡加載,除非本地有當前 url 對應有緩存,否則就會加載。 */
@Override
publicvoidonLoadResource(WebView view, String url){
super.onLoadResource(view, url);
? ? ? ? }
/** * WebView 可以攔截某一次的 request 來返回我們自己加載的數據,這個方法在后面緩存會有很大作用。*/
@Override
publicWebResourceResponseshouldInterceptRequest(WebView view, WebResourceRequest request){
returnsuper.shouldInterceptRequest(view, request);
? ? ? ? }
/** * WebView 訪問 url 出錯 */
@Override
publicvoidonReceivedError(WebView view, WebResourceRequest request, WebResourceError error){
super.onReceivedError(view, request, error);
? ? ? ? }
/*** WebView ssl 訪問證書出錯,handler.cancel()取消加載,handler.proceed()對然錯誤也繼續加載*/
@Override
publicvoidonReceivedSslError(WebView view, SslErrorHandler handler, SslError error){
super.onReceivedSslError(view, handler, error);
? ? ? ? }
? ? }
????????三、與JS交互
????????1、Android調用js方法:2種方式
????js方法:
functioncallJS(){?
?? ? ? alert("Android調用了JS的callJS方法");
?}
????????(1)第一種方法:loadUrl
wvWebview.loadUrl("javascript:callJS()");
????????(2)第二種方法:evaluateJavascript
wvWebview.evaluateJavascript("javascript:callJS()",newValueCallback() {
@Override
publicvoidonReceiveValue(String value){
//此處為 js 返回的結果
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
????????因為該方法的執行不會使頁面刷新,而第一種方法(loadUrl )的執行則會。Android 4.4 后才可使用
????????所以建議:兩種方法混合使用,即Android 4.4以下使用方法1,Android 4.4以上方法2
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
wvWebview.evaluateJavascript("javascript:callJS()",newValueCallback() {
@Override
publicvoidonReceiveValue(String value){
//此處為 js 返回的結果
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
}else{//Android 4.4 前使用該方法
wvWebview.loadUrl("javascript:callJS()");
? ? ? ? }
????????2、js調用Android方法:3種方式
????????(1)第一種方法:addJavascriptInterface
????????優點:使用簡單
????????缺點:存在嚴重的漏洞問題
// js調用方式
? ? function callAndroid(){
? ? ? ? test.hello("js調用了android中的hello方法");
? ? }
//通過WebView設置Android類與JS代碼的映射
wvWebview.addJavascriptInterface(new AndroidtoJs(), "test");
// 定義JS需要調用的方法
public class AndroidtoJs extends Object {
? ? ? ? // 被JS調用的方法必須加入@JavascriptInterface注解
? ? ? ? @JavascriptInterface
? ? ? ? public void hello(String msg) {
? ? ? ? ? ? System.out.println("JS調用了Android的hello方法");
? ? ? ? }
}
????????(2)第二種方法:通過 WebViewClient 的shouldOverrideUrlLoading ()方法回調攔截 url
//js調用
? ? function callAndroid(){
? ? ? ? document.location="js://webview?arg1=1&arg2=222";
? ? }
// andorid
// 在Android通過WebViewClient復寫shouldOverrideUrlLoading ()
public class MyWebViewClient extends WebViewClient {
? ? ? ? /**
? ? ? ? * 是否在 WebView 內加載頁面
? ? ? ? */
? ? @Override
? ? public boolean shouldOverrideUrlLoading(WebView view, String url) {
? ? ? ? ? ? view.loadUrl(url);
? ? ? ? ? ? // 步驟2:根據協議的參數,判斷是否是所需要的url
? ? ? ? ? ? // 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)
? ? ? ? ? ? //假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)
? ? ? ? ? ? Uri uri = Uri.parse(url);
? ? ? ? ? ? // 如果url的協議 = 預先約定的 js 協議
? ? ? ? ? ? // 就解析往下解析參數
? ? ? ? ? ? if (uri.getScheme().equals("js")) {
? ? ? ? ? ? ? ? // 如果 authority? = 預先約定協議里的 webview,即代表都符合約定的協議
? ? ? ? ? ? ? ? // 所以攔截url,下面JS開始調用Android需要的方法
? ? ? ? ? ? ? ? if (uri.getAuthority().equals("webview")) {
? ? ? ? ? ? ? ? ? ? // 步驟3:
? ? ? ? ? ? ? ? ? ? // 執行JS所需要調用的邏輯
? ? ? ? ? ? ? ? ? ? System.out.println("js調用了Android的方法");
? ? ? ? ? ? ? ? ? ? // 可以在協議上帶有參數并傳遞到Android上
? ? ? ? ? ? ? ? ? ? HashMap params = new HashMap<>();
? ? ? ? ? ? ? ? ? ? Set collection = uri.getQueryParameterNames();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? return super.shouldOverrideUrlLoading(view, url);
? ? ? ? }
}
????????(3)第三種方法:通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt() 消息
//js調用
? ? function clickprompt(){
? ? ? ? var result = prompt("js://demo?arg1=111&arg2=222");
? ? ? ? alert("demo" + result);
? ? }
//android定義
public class MyWebChromeClient extends WebChromeClient {
? ? @Override
? ? public boolean onJsPrompt(WebView view, String url, String message, String? ? ? ? ? ? defaultValue, JsPromptResult result) {
? ? ? ? ? ? // 根據協議的參數,判斷是否是所需要的url(原理同方式2)
? ? ? ? ? ? // 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)
? ? ? ? ? ? //假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)
? ? ? ? ? ? Uri uri = Uri.parse(message);
? ? ? ? ? ? // 如果url的協議 = 預先約定的 js 協議
? ? ? ? ? ? // 就解析往下解析參數
? ? ? ? ? ? if (uri.getScheme().equals("js")) {
? ? ? ? ? ? ? ? // 如果 authority? = 預先約定協議里的 webview,即代表都符合約定的協議
? ? ? ? ? ? ? ? // 所以攔截url,下面JS開始調用Android需要的方法
? ? ? ? ? ? ? ? if (uri.getAuthority().equals("webview")) {
? ? ? ? ? ? ? ? ? ? // 執行JS所需要調用的邏輯
? ? ? ? ? ? ? ? ? ? System.out.println("js調用了Android的方法");
? ? ? ? ? ? ? ? ? ? // 可以在協議上帶有參數并傳遞到Android上
? ? ? ? ? ? ? ? ? ? HashMap params = new HashMap<>();
? ? ? ? ? ? ? ? ? ? Set collection = uri.getQueryParameterNames();
? ? ? ? ? ? ? ? ? ? //參數result:代表消息框的返回值(輸入值)
? ? ? ? ? ? ? ? ? ? result.confirm("js調用了Android的方法成功啦");
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? return super.onJsPrompt(view, url, message, defaultValue, result);
? ? ? ? }
}
????四、開源庫
因為WebView 本身功能不是很完善而且用起來也是極其麻煩,所以這里推薦2個開源庫:JsBridge與AgentWeb
JSBridge:http://www.lxweimin.com/p/2ec3f06d6087
AgentWeb:http://www.lxweimin.com/p/c80da1c41af7
WebView開源庫地址:
https://github.com/lzyzsd/JsBridge
https://github.com/Justson/AgentWeb
注:參考文章
http://www.lxweimin.com/p/3c94ae673e2aWebview?使用攻略
http://www.lxweimin.com/p/fd61e8f4049eWebview?干貨
http://www.lxweimin.com/p/345f4d8a5cfaAndroid WebView與 JS 的交互方式
http://www.lxweimin.com/p/3a345d27cd42Android WebView 使用漏洞
http://www.lxweimin.com/p/5e7075f4875fWebView 緩存機制