Android 原生 WebView 與 JavaScript 互動

PandaQ發表於2017-07-07

現在的純原生的 APP 很少見了,幾乎都是原生中嵌者網頁的 hybird APP,或者直接使用 React Native 開發。原生 App 中嵌者網頁原生與網頁中 JavaScript 的互動幾乎無法避免,畢竟在自己的應用中嵌入網頁不只是想讓應用當個瀏覽器用。
Android 原生中載入網頁的控制元件是 WebView,因此要想原生與 JavaScript 互動也一樣必須通過 WebView 來實現。

相關類和主要方法

WebView

低版本和高版本的 Android 系統中 WebView 採用不同的核心, Android 4.4 之後直接使用了 Chrome 的核心。
常用方法:

//根據 url 去載入一個網頁
webView.loadUrl(String url);

//根據 HTML 資料顯示出內容,其中 loadData() 方法的 data 中不能出現英文的 %,#,\,? 四個字元,否則可能會報錯
webView.loadData(String data,String mimeType,String encoding);
webView.loadDataWithBaseUrl(String baseUrl,String mimeType,String encoding,String historyUrl);

// webView 處於啟用狀態,能正常載入和響應網頁
webView.onResume();

// webView 處於暫停狀態,當頁面失去焦點切換到後臺時呼叫
// 處於 pause 狀態的 WebView 會停止動畫和計算,但是不會停止 JavaScript 的執行
webView.onPause();

//暫停所有 WebView 的 layout、parsing、JavaScriptTimers 以降低 CPU 消耗(全域性有效)
webView.pauseTimers();

// 恢復 pauseTimers 的暫停狀態
webView.resumeTimers();

// 銷燬一個 WebView 時要先將 WebView 和當前介面解綁再銷燬
parentLayout.removeView(webView);
webView.destroy();

// 判斷網頁是否可以後退(可以實現監聽,當網頁可以後退事點選返回鍵和返回按鈕不退出瀏覽介面)
webView.canGoBack();

//後退網頁
webView.goBack();

// 判斷網頁是否可以前進
webView.canGoForward();

//前進網頁
webView.goForward();

//以當前頁為起點前進後退 steps 個頁面(正數前進,負數後退)
webView.goBackOrForward(int steps);複製程式碼

WebSetting

WebSetting 是為 WebView 提供配置和管理的一個抽象類,它通過 webView.getSettings() 方法獲取例項。
常用方法:

// 設定 webView 是否支援 JavaScript 的呼叫(應用中涉及原生與 JS 互動的必須設定為 true)
webSettings.setJavaScriptEnabled(true);

// 設定是否允許 JS 開啟新視窗(function window.open())
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

// 設定 webView 是否支援外掛
webSettings.setPluginsEnable(true);

// 設定編碼方式

// 設定是否自適應螢幕,(一般圖片和網頁縮放同時使用)
webSettings.setUseWideViewPort(true); //將圖片調整到適合 webView 的大小
webSettings.setLoadWithOverviewMode(true); //縮放至螢幕的大小(顯示未做移動端相容的 PC 網頁)

// 設定是否支援縮放,預設為支援
webSettings.setSupportZoom(true);
// 設定內建的縮放控制元件
webSettings.setBuiltInZoomControls(true);
// 隱藏原生的縮放控制元件
webSettings.setDisplayZoomControls(true);複製程式碼

WebViewClient

WebViewClient 主要用來幫助 WebView 處理請求的各種狀態事件。
常用方法:

// 頁面開始載入的時候回撥
onPageStarted(WebView view, String url, Bitmap favicon);
// 頁面載入結束時回撥
onPageFinished(WebView view, String url);
// 將要載入資源時回撥,每次資源的載入都會呼叫
onLoadResource(WebView view, String url);
// 載入的頁面 404 時將會回撥
onReceivedError(WebView view, WebResourceRequest request, WebResourceError error);複製程式碼

webView 預設情況下是不會載入 https 請求的,頁面將顯示空白,如果載入 https 頁面需要進行如下設定:

webView.setWebViewClient(new WebViewClient() {    
        @Override    
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {    
            handler.proceed();    //表示等待證照響應
        // handler.cancel();      //表示掛起連線,為預設方式
        // handler.handleMessage(null);    //可做其他處理
        }    
    });複製程式碼

WebChromeClient

輔助 WebView 處理 JavaScript 對話方塊,載入進度,網頁圖示等。
常用方法:

// 載入進度回撥
onProgressChanged(WebView view, int newProgress);
// 獲取到網頁 Title 回撥
onReceivedTitle(WebView view, String title);
// 獲取到網頁圖示回撥
onReceivedIcon(WebView view, Bitmap icon);複製程式碼

WebView 的使用

通過前面的瞭解,WebView 及他的輔助類的一些基本方法我們是知道了,接下來就看一下一下怎麼使用 WebView 以及怎樣通過 WebView 讓 Android 源生程式碼與 JavaScript 程式碼進行互動。

JS 呼叫原生方法

JS 呼叫源生的方法時該方法必須要加上 @JavascriptInterface 註解,我們可以定義一個類或者介面來單獨存放用於 JS 呼叫的方法,這裡我以介面為例。介面中提供一個 getUrl(String url) 方法用於提供給 JS 呼叫,傳遞一個 String 型別的值給源生方法:

/**
 * Created by PandaQ on 2017/3/22.
 * JS 介面方法定義
 * 介面中定義方法時不用加註解,使用時才加註解
 */
public interface JavaScriptFunction {
    void getUrl(String string);
}複製程式碼
// 此程式碼片段為 PandaEye 中點選載入的 html 資料中的圖片跳轉到新的 Activity 顯示圖片的功能
webView.addJavascriptInterface(new JavaScriptFunction() {
            @Override
            @JavascriptInterface // 加上註解 getUrl() 方法才能被 JS 呼叫
            public void getUrl(String imageUrl) {
                LogWritter.LogStr(imageUrl);
                Intent intent = new Intent();
                intent.putExtra("imageUrls", mImageUrls);
                intent.putExtra("curImageUrl", imageUrl);
                intent.setClass(ZhihuStoryInfoActivity.this, PhotoViewActivity.class);
                startActivity(intent);
            }
        }, "JavaScriptFunction");複製程式碼
// 在 HTML 中圖片的點選事件 JS 方法中就可以執行如下程式碼來呼叫源生的介面方法
function(){
    window.JavaScriptFunction.getUrl(this.src);
}複製程式碼

原生呼叫 JS

在熊貓眼 PandaEye 中因為使用的知乎日報和網易新聞的 API 所以下發的 HTML body 資料需要我們自己加個 HTML 的殼,然後再源生載入我們需要的 JavaScrpit 程式碼來實現我們的功能。
載入 JS 可以直接 webView.load() 載入完整的 JS 程式碼也可以在自己加的 head 節點引入 JS 檔案然後 webView 直接 load 其中的方法, PandaEye 中使用的是第二種方法:
在拼裝 HTML 時在 head 中引入放在 asset 資料夾中的 js 檔案

enter description here
enter description here

imageClick.js 中的內容如下:

function initClick()
{
    var objects = document.getElementsByTagName("img");
    for(var i=0;i<objects.length;i++)
    {
        objects[i].onclick= function (){
            window.JavaScriptFunction.getUrl(this.src);
        }
    }
}複製程式碼

然後在 WebChromeClient 當載入進度達到 100% 後去呼叫 JS 檔案中的 initClick() 方法,為每一張圖片設定點選事件:

    /**
     * 為所有的圖片新增點選事件
     *
     * @param webView 對應的 WebView
     */
    private void addImageClickListener(WebView webView) {
        webView.loadUrl("javascript:(initClick())()");
    }複製程式碼

直接載入 JS 把 initClick() 用 JS 程式碼替換掉即可。

利用 Chrome 除錯 WebView

WebView 中 JavaScript 程式碼的除錯直接使用 AndroidStudio 是沒辦法的,那麼我們怎樣除錯 HTML 頁面呢?答案是用 Chrome 瀏覽器來除錯:

  • USB 選項是開啟的(AS 能除錯應用就行)
  • Chrome 瀏覽器開啟地址:chrome://inspect 或者 about:inspect
    開啟待除錯的 WebView 然後可以看到如下介面:

InspectPage
InspectPage

點選對應頁面的 inspect 就可以進入除錯頁面:

DebugPage
DebugPage

接下里就可以像除錯前端頁面一樣除錯 WebView 中的內容了。

最後

詳細使用程式碼請移步 PandaEye 檢視

相關文章