WebView初使用心得

基本設置

WebView可以實現將網頁嵌入到爲應用程序專門設計的環境中。大多數情況還是建議使用標準的網絡瀏覽器將內容交互給用戶。WebView缺少一些完全開發的瀏覽器功能。

private Context mContext;
private WebView mWebView;
/**
 * WebView父親佈局
 */
private LinearLayout llContainerWeb;

//可以使用addView的方式,也可以使用xml方式,只要記得必要的時候移除view、清除緩存和置空即可
mWebView = new WebView(mContext);
ViewGroup.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
//把webView加入llContainerWeb
llContainerWeb.addView(mWebView, layoutParams);

//不顯示橫向滑動條
mWebView.setHorizontalScrollBarEnabled(false);
//不顯示縱向滑動條
mWebView.setVerticalScrollBarEnabled(false);

WebSettings webSettings = mWebView.getSettings();
//設置是否啓用javascript
webSettings.setJavaScriptEnabled(true);
//是否支持縮放(這個是前提)
webSettings.setSupportZoom(false);
//是否顯示縮放工具(當上門那個成立,再設置這個,必要條件)
webSettings.setBuiltInZoomControls(false);
//設置此屬性,可任意比例縮放
webSettings.setUseWideViewPort(false);
//設置充滿全屏
webSettings.setLoadWithOverviewMode(true);
//把所有內容放到WebView組件等寬的一列中
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
//設置字體默認大小
/*webSettings.setDefaultFontSize(15);*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //設置不允許安全來源從不安全的位置加載內容起源,非常建議使用這個。
    //webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
    //不建議使用這個,從任何來源加載內容。5.1以上默認禁止了https和http混用 這是開啓
    //webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//如下設置是解決Cross origin requests are only supported for protocol schemes: http, data, chrome, https.這種問題的。
//webSettings.setAllowFileAccess(true);
//webSettings.setAllowFileAccessFromFileURLs(true);
//能否訪問來自於任何源的文件標識的URL
//webSettings.setAllowUniversalAccessFromFileURLs(true);

//設置WebViewClient,可以攔截url,做一些處理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            //攔截url:根據協議的參數,判斷是否是所需要的url
            //比如約定好的字段爲"https://www.haizhuo.com/user?name=hz&id=15"
            Uri uri = request.getUrl();
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }
            return super.shouldOverrideUrlLoading(view, request);
        }
    });
} else {
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Uri uri = Uri.parse(url);
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }
            return super.shouldOverrideUrlLoading(view, url);
        }
    });
}

//設置WebChromeClient,可以獲得js回調、進度、標題、圖標等的回調。
mWebView.setWebChromeClient(new WebChromeClient(){
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        return super.onJsAlert(view, url, message, result);
    }

    @Override
    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
        return super.onJsConfirm(view, url, message, result);
    }

    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        return super.onJsPrompt(view, url, message, defaultValue, result);
    }

    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
    }

    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
    }
});

mWebView.loadUrl("https://www.baidu.com/");

//直接加載assets目錄下的html網頁,並且與網頁有關的css,js,圖片等文件會加載。
//mWebView.loadUrl("file:///android_asset/test/test.html");


@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (event.getKeyCode()==KeyEvent.KEYCODE_BACK&&mWebView.canGoBack()){
        //獲取webView的瀏覽記錄
        WebBackForwardList webBackForwardList=mWebView.copyBackForwardList();
        //有上一個界面,就跳轉上一個界面
        if (webBackForwardList.getCurrentIndex()>0){
            mWebView.goBack();
            return true;
        }
    }
    return super.onKeyDown(keyCode, event);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //使用完畢,清除webview緩存和歷史,避免內存泄露
    mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
    mWebView.clearHistory();
//    mWebView.clearCache(true);
//    mWebView.clearSslPreferences();
    //不要在android.graphics.Canvas#draw、dispatchDraw、onDraw方法裏調用此方法
//    webView.removeAllViews();
    
    ((ViewGroup) mWebView.getParent()).removeView(mWebView);
    mWebView.destroy();
    mWebView = null;
}

Android調用JS代碼

注意事項:JS代碼調用要在 onPageFinished() 回調之後。

通過WebView的loadUrl()

一般樣式

String url = "javascript:methodName(params……)"; 
webView.loadUrl(url); 

例1:調用網頁端寫好的方法

<script type="text/javascript">
    function testPop(){
            alert("17張牌你能秒我?");
    }
</script>
mWebView.loadUrl("javascript:testPop()"); 

例2:調用在客戶端寫的方法(網頁端沒有)

//設置網頁中所有標籤名字爲‘img’的對象高寬
mWebView.loadUrl("javascript:(function(){" +
                "var objs = document.getElementsByTagName('img'); " +
                "for(var i=0;i<objs.length;i++)  " +
                "{"
                + "var img = objs[i];   " +
                "    img.style.maxWidth = '100%'; img.style.height = 'auto';  " +
                "}" +
                "})()");

通過WebView的evaluateJavascript()

mWebView.evaluateJavascript("javascript:test()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此處爲返回的結果
        }
    });

2個方法對比

image

JS通過WebView調用 Android 代碼

不管通過什麼方式,雙方都需要做一個約定。比如通過WebChromeClient攔截JS對話框消息,當消息是某約定的字段,Android客戶端做某個操作;再如通過WebViewClient攔截url,當網頁跳轉到某個約定的地址時,Android客戶端做某個操作;還有WebView方法addJavascriptInterface,也要約定對象映射的名字,這樣,js端才能通過這個“名字”的對象調用方法。

1. 通過對象映射

WebView的addJavascriptInterface()。addJavascriptInterface是WebKit的原生API,屬於WebView對象的公共方法,用於暴露一個java對象給js,使得js可以直接調用方法。當然,java與js的相互調用也離不開loadUrl()方法的配合使用。

例子

先寫一個映射關係的類

class MyJsInterfaceTest{


    //API 17以後一定要添加註解,否則addJavascriptInterface方法會失效。
    @SuppressLint("JavascriptInterface")
    @JavascriptInterface
    public String getAppNameFromAndroid(){
        return MyApplication.getInstance().getAppName();
    }
}

然後設置WebView

webView.getSettings().setJavaScriptEnabled(true);
//...省略部分代碼
webView.addJavascriptInterface(new MyJsInterfaceTest(), "MyJsInterfaceTest");

在html裏通過映射關係類調用方法

<script type="text/javascript">
        //調用Android端代碼獲取名字,並彈框顯示
        function getName(){
             alert(myJsTest.getName());
        }
</script>
//...省略部分代碼
<button class="bt_todo_java" id="button_one" onclick="getName()">js調用Android代碼</button>

2. 攔截url

WebViewClient 的shouldOverrideUrlLoading ()方法

當即將在當前WebView中加載URL時,使主機應用程序有機會控制。 如果未提供WebViewClient,則默認情況下,WebView將要求“活動管理器”爲URL選擇適當的處理程序。 如果提供了WebViewClient,則返回true會導致當前WebView放棄加載URL,而返回false會導致WebView照常繼續加載URL。

WebView在加載一個頁面開始的時候會回調onPageStarted方法,在該頁面加載完成之後會回調onPageFinished方法。而如果該鏈接發生了重定向,回調shouldOverrideUrlLoading會在回調onPageFinished之前。

示例

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    mWebview.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            //攔截url:根據協議的參數,判斷是否是所需要的url
            //比如約定好的字段爲"https://www.haizhuo.com/user?name=hz&id=15"
            Uri uri = request.getUrl();
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }

            return super.shouldOverrideUrlLoading(view, request);
        }
    });
} else {
    mWebview.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Uri uri = Uri.parse(url);
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }
            return super.shouldOverrideUrlLoading(view, url);
        }
    });
}

shouldOverrideUrlLoading不執行的情況

通常情況,直接與用戶交互,跳轉的地址,shouldOverrideUrlLoading是能攔截到的。
延時跳轉的話(比如在某個回調裏跳轉),之後的跳轉shouldOverrideUrlLoading都攔截不到了,但會將請求的頁面加載到webview中

以example.html爲例

//...省略其他代碼

function goToResultPage(){
    //本頁跳轉
    window.location.href='http://www.myresult.com.cn';
}


//延時跳轉
setTimeout(goToResultPage, 3000);

分析:正常操作到example.html,url都有攔截到,但是跳轉到http://www.myresult.com.cn及他以後的地址shouldOverrideUrlLoading都不會有回調了。

3. 攔截JS對話框

WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt() 消息
除此之外,還有onProgressChanged()、onReceivedTitle()、onReceivedTouchIconUrl()等一些列方法。

上述3方法對比

image

補充知識點

WebViewClient

WebViewClient這個組件主要用來輔助webview處理一些簡單的html頁面內容,比如各種通知,請求事件等等,也就是說在用這個組件你可以監聽網頁加載是否完成,是否開始等等,然後還有就是可以設置是否在外部瀏覽器中瀏覽。

onLoadResource、onPageStart、onPageFinish、onReceiveError、onReceivedHttpAuthRequest、shouldOverrideUrlLoading

WebChromeClient

WebChromeClient可以設置標題啥的。

關於在js中打印日誌,java中獲取打印信息以及提示、打印回調
  • 在 Javascript 代碼中調用console.log(‘xxx’)方法,中Native代碼會回調WebChromeClient.consoleMessage()
  • JS中調用window.prompt(message, value),WebChromeClient.onJsPrompt()就會收到回調信息
  • JS中調用 alert(“信息”),WebChromeClient.onJsAlert()就會收到回調信息

除上面幾個外,再記錄一些(不是全部):onReceivedTitle、onReceivedIcon、onReceivedTouchIconUrl、onHideCustomView、onRequestFocus、onCloseWindow、onJsBeforeUnload、onGeolocationPermissionsShowPrompt、onGeolocationPermissionsHidePrompt、onPermissionRequest、onPermissionRequestCanceled、onConsoleMessage、getDefaultVideoPoster、getVisitedHistory等

跨域

什麼是跨域?什麼是Cross-origin_resource_sharing?

跨域請求存在的原因:由於瀏覽器的同源策略,即屬於不同域的頁面之間不能相互訪問各自的頁面內容。

跨域使用的場景?

  • 域名不同
    www.jiuyescm.com和www.jiuye.com即爲不同的域名
  • 二級域名相同,子域名不同
    a.jiuyescm.com和b.jiuyescm.com爲子域不同
  • 端口不同,協議不同
    http://www.jiuyescm.com 和 https://www.jiuyescm.com
    www.jiuyescm.com:8888和www.jiuyescm.com:8080

解決跨域的方式?

  • jsonp
    安全性差,已經不推薦
  • CORS(W3C標準,跨域資源共享 - Cross-origin resource sharing)
    服務端設置,安全性高,推薦使用
  • websocke
    特殊場景時使用,不屬於常規跨域操作
  • 代理服務(nginx)
    可作爲服務端cors配置的一種方式,推薦使用

參考文章

最全面總結 Android WebView與 JS 的交互方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章