Android與WebView資料互動

塔歌發表於2018-07-24

1. 建立Android 專案

    1. 開啟Android Studio

-w450

    1. 建立一個空的Android專案

-w450

    1. 開啟Android虛擬機器,這裡使用的是Genymotion

15268784107324.jpg

2. 新增webview

    1. 清空layout內容,新增WebView控制元件
<WebView
        android:id="@+id/web"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    1. MainActivity中建立引入webview
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    WebView mweb = findViewById(R.id.web);
    mweb.loadUrl("http://www.baidu.com");
}
    1. 此時執行專案,發現android安裝成功,webview也可以開啟,但是頁面請求失敗,需要給APP新增聯網許可權。
      AndroidManifest.xmlmanifest標籤內新增
<uses-permission android:name="android.permission.INTERNET"/>
    1. 此時執行專案,發現webview可以開啟並且百度頁面可以訪問了,但是仔細發現webview並不是在自己的APP上開啟的,而是彈出系統瀏覽器。因為webview只是載體,內容的渲染需要使用webviewChromClient類去實現此時需要setWebViewClient
...
WebView mweb = findViewById(R.id.web);
mweb.setWebViewClient(new WebViewClient());
mweb.loadUrl("http://www.baidu.com");
...
    1. 執行APP後可以看到webview已經差不多是我們想要的了

15268792310451.jpg

3. 設定JavaScript可執行

仔細看會發現,通過上面步驟開啟的百度首頁跟我們平時看到的廣告頁面不一樣,原因是WebView預設禁止了JS的執行。這也是我覺得百度首頁做的比較好的地方,在無JS環境下仍可提供服務。

  • 新增setJavaScriptEnabled
...
mweb.setWebViewClient(new WebViewClient());
mweb.getSettings().setJavaScriptEnabled(true);
mweb.loadUrl("http://www.baidu.com");
...

此時再開啟頁面,就會發現廣告出來了,並且頁面很卡。

15268796309520.jpg

4. WebView 呼叫 Android 方法

1. 使用addJavascriptInterface

    1. 新增JS介面
      建立JavaScriptInterFace.java類,用來寫JS呼叫方法
package com.test.myapplication;

import android.util.Log;
import android.webkit.JavascriptInterface;

public class JavaScriptInterFace {

    @JavascriptInterface
    public String getValue(String name) {
        Log.d("tagee", "getValue:" + name);
        return "call back";
    }
}

因為安全問題,在Android4.2中JS只能訪問帶有 @JavascriptInterface註解的Java函式。

    1. addJavascriptInterface注入
...
mweb.setWebViewClient(new WebViewClient());
mweb.getSettings().setJavaScriptEnabled(true);
mweb.addJavascriptInterface(new JavaScriptInterFace(), "JSBridge"); 
// JSBridge 為 webview 中呼叫的物件名稱
mweb.loadUrl("http://www.baidu.com");
...
    1. 載入本地 html 檔案

app/src/main目錄下面建立目錄assets並新建a.html檔案,寫入如下程式碼

<!doctype html>
<html lang="en">
    <head></head>
    <body>
        <h1 id="h1">123</h1>
        <script>
            if(window.JSBridge){
                alert(JSBridge.getValue(`from JS`));
            }
        </script>
    </body>
</html>

修改webview的loadUrl引數

mweb.addJavascriptInterface(new JavaScriptInterFace(), "JSBridge"); 
mweb.loadUrl("file:///android_asset/a.html");

此時重新build,可以看到Logcat列印出from JS,並且頁面彈窗。

2.通過loadUrl()

loadUrl可以執行JavaScript程式碼,他有如下特徵:

1. 呼叫`loadUrl`會重新整理頁面
2. 當引數為要執行的JS程式碼時,要有document物件,至少得load一個空白頁,否則會失效
3. 若返回值為非空字串,則會將返回值替換頁面原本的內容
4. 可以呼叫html中的js程式碼,但需要在`onPageFinished`回撥之後才能呼叫,並且注意第三點的影響,防止返回字串替換文件

如:

...
mweb.addJavascriptInterface(new JavaScriptInterFace(), "JSBridge"); 
mweb.loadUrl("javascript:aler(123)"); //因當前webview沒用載入任何頁面,指令碼無效
...
mweb.addJavascriptInterface(new JavaScriptInterFace(), "JSBridge"); 
mweb.loadUrl("file:///android_asset/a.html");
mweb.loadUrl("javascript:`123`"); //此時頁面被替換成123
protected void onCreate(Bundle savedInstanceState) {
    ...
    mweb.addJavascriptInterface(new JavaScriptInterFace(), "JSBridge"); 
    mweb.setWebViewClient(new mWebViewClient());
    mweb.loadUrl("file:///android_asset/a.html");
}
...
private class mWebViewClient extends WebViewClient{
    @Override
    public void onPageFinished(WebView view, String url) {
        view.loadUrl("javascript:callJS()");//callJS為a.html中定義的方法
        super.onPageFinished(view, url);
    }
} 

實際使用時,Android更多的是呼叫遠端JS程式碼,即將載入的JS程式碼路徑改成url即可。或則直接拼接JS程式碼時也會放在閉包中執行,防止替換頁面內容。

3. 通過evaluateJavascript()

如果是Android4.4後,推薦使用evaluateJavascript,比loadUrl效率更高,並且不會重新整理頁面。
evaluateJavascript有兩個引數,第一個為指令碼內容,第二個則是指令碼的執行結果。在onPageFinished呼叫:

view.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
        Log.d("tagee", value);
    }
});

4. 通過shouldOverrideUrlLoading()

shouldOverrideUrlLoadingWebViewClient物件的一個”生命週期”,用攔截URL的請求,監聽網頁地址的變化,返回turefalse來決定webview是否載入URL。

1. 若沒有設定 WebViewClient 則由系統(Activity Manager)處理該 url,通常是使用瀏覽器開啟或彈出瀏覽器選擇對話方塊,即出現上文2.4的情況。
2. 若設定 WebViewClient 且該方法返回 true ,則說明由應用的程式碼處理該 url,WebView不跳轉。 
3. 若設定 WebViewClient 且該方法返回 false,則說明由 WebView 處理該 url,即用 WebView 載入該 url。 
private class mWebViewClient extends WebViewClient{
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
     
        //此處可以解析url進行處理,
        //也可以直接呼叫view.loadUrl(url); 在當前的webview中跳轉到新的url
        //若前端直接location.href="js://web?param1=123&param2=asd"
        Uri uri = Uri.parse(url);
        if ( uri.getScheme().equals("js")) {
            //可以在此處獲取頁面請求資料並處理
            //或則直接跳轉activity等
            return true;
        }
        return false;
    }
} 

值得一提的是以前一直有一個誤區,以為使用一個不可見的iframe標籤,再動態改變其src也可以捕捉url變化達到傳值的目的。測試過不可行,包括預設寫入和動態建立iframe

4. 通過onJsAlert()、onJsConfirm()、onJsPrompt()

原理和上面一條一樣,通過監聽頁面的彈窗事件,達到傳值的目的,不細說了。


相關文章