1. 程式人生 > >Android 實現WebView與JavaScript的互相呼叫

Android 實現WebView與JavaScript的互相呼叫

很多複雜的UI介面,在Android中需要配合大量xml程式碼和java程式碼實現,而使用HTML5可以非常輕鬆的實現出來,而且具有很好的跨平臺特性,讓我們不必為了多個平臺而重寫程式碼,H5學習成本也較低,上手快。雖然從目前來說H5在Android系統中的速度可能還欠佳一些,但相信隨著手機的效能不斷的提高,這些問題都會被解決

使用H5開發Android的UI介面,最重要的就是如何實現Js程式碼和Java程式碼之間的互相呼叫了

在講解之前,讓我們先把專案跑起來
效果圖:
這裡寫圖片描述

準備好index.html檔案,將它放入Android工程下的assets資料夾中:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSTest</title> <script src="app.js"></script> </head> <body> <table border="1" width="100%" id="table" cellspacing="0"> <tr> <td
width="50%" align="center">
姓名</td> <td width="50%" align="center">電話</td> </tr> </table> <hr> <input id="jsinput"> <Button onclick="getMessage()">js傳值給Toast</Button> </body> </html>

JavaScript的程式碼我單獨寫在一個js檔案中了,把app.js檔案也放入assets資料夾中:

function getMessage(){
    var message = document.getElementById("jsinput");
    contact.showToast(message.value);
}

function addPerson(persons){
    var personObjs = eval(persons);
    var table = document.getElementById("table");
    for(var i=0; i < personObjs.length; i++){
        var tr = table.insertRow(table.rows.length);
        var td1 = tr.insertCell(0);
        td1.align = "center";
        var td2 = tr.insertCell(1);
        td2.align = "center";

        td1.innerHTML = personObjs[i].name;
        td2.innerHTML = personObjs[i].phone;
    }
}

最後就是Java程式碼

public class MainActivity extends AppCompatActivity {

    private WebView mWebView;
    private Button mJsMethodBtn;
    private JsObject jsobj;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mJsMethodBtn = (Button) findViewById(R.id.btn_js_method);
        mWebView = (WebView) findViewById(R.id.web_view);

        mWebView.loadUrl("file:///android_asset/index.html");
        WebSettings setting = mWebView.getSettings();
        setting.setJavaScriptEnabled(true);
        setting.setDefaultTextEncodingName("utf-8");

        jsobj = new JsObject();
        mWebView.addJavascriptInterface(jsobj, "contact");

        mJsMethodBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 新增一個聯絡人
                jsobj.addPerson();
            }
        });
    }

    private class JsObject {
        // 此方法被js呼叫
        @JavascriptInterface
        public void showToast(String message) {
            Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
        }
        // 在Web頁面增加一個聯絡人
        public void addPerson() {
            String json = "[{\"name\":\"zwt\",\"phone\":\"15949999999\"}]";
            mWebView.loadUrl("javascript:addPerson('" + json + "')");
        }
    }    
}

還有佈局程式碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#EED5B7"
    android:orientation="vertical">

    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="360dp"/>

    <Button
        android:id="@+id/btn_js_method"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="呼叫js方法"/>

</LinearLayout>

一、JavaScript呼叫Android中的方法

這裡實現的場景是點選Web頁面中的Button,把input中輸入的資料傳遞給Android系統,並通過Toast顯示出來

對應的js程式碼:

function getMessage(){
    var message = document.getElementById("jsinput");
    contact.showToast(message.value);
}

對應的java程式碼:

// 此方法被js呼叫
@JavascriptInterface
public void showToast(String message) {
    Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}

其中的“contact”其實指的就是我們在java程式碼中定義的JsObject類
他倆通過以下方法實現繫結:

mWebView.addJavascriptInterface(jsobj, "contact");
  • 第一個引數傳入的是一個java物件,第二引數是指定對應的js裡呼叫該類時需要使用的自定義別名,這個方法的作用就是將一個Java物件和JavaScript聯絡起來

  • 這裡需要注意個問題,在SDK17以上的版本中,google為了安全考慮,只允許js呼叫帶有@JavascriptInterface註解的Java方法,所以我們要給被js呼叫的java方法前加上@JavascriptInterface註解

二、Android呼叫JavaScript中的方法

使用者點選Android中的Button控制元件後,傳一個json資料給JavaScript方法,js解析json資料後新增一個新的聯絡人顯示在Web頁面上

對應的js程式碼:

function addPerson(persons){
    var personObjs = eval(persons);
    var table = document.getElementById("table");
    for(var i=0; i < personObjs.length; i++){
        var tr = table.insertRow(table.rows.length);
        var td1 = tr.insertCell(0);
        td1.align = "center";
        var td2 = tr.insertCell(1);
        td2.align = "center";

        td1.innerHTML = personObjs[i].name;
        td2.innerHTML = personObjs[i].phone;
    }
}

對應的java程式碼:

// 在Web頁面增加一個聯絡人
public void addPerson() {
    String json = "[{\"name\":\"zwt\",\"phone\":\"15949999999\"}]";
    mWebView.loadUrl("javascript:addPerson('" + json + "')");
}

想要呼叫JavaScript中的某個方法,使用以下方法的標準格式就可以了:

mWebView.loadUrl("javascript:xxxMethod()");

“xxxMethod()”指的是JavaScript中的某個方法,如需呼叫其它方法,只要把後面的xxxMethod()替換成js中對應的方法就好

三、常見問題

1.Android與js互調不成功

  • 給WebView的setJavaScriptEnabled方法設定為true,使其允許js程式碼執行

  • 在API高於17的版本上,需要被js呼叫的java方法前加上@JavascriptInterface

  • 檢查js中的別名是否寫錯,呼叫java方法時類的別名,一定要是mWebView.addJavascriptInterface(jsobj, “contact”);裡面定義的別名

2.網頁的alert彈不出
需要重寫WebChromeClient中的onJsAlert()方法

mWebView.setWebChromeClient(new WebChromeClient() {
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        return super.onJsAlert(view, url, message, result);
    }
});

如果需要把web頁面的alert彈出框替換成Android的AlertDialog,可以在onJsAlert()方法裡進行重寫,並設定return為true

3.Js呼叫java方法修改UI介面不成功
只要明白這一點:js呼叫的java方法,其實是執行在另外一個子執行緒WebViewCoreThread中
測試一下:把以下語句分別放在Activity的onCreate()方法裡和被js呼叫的java方法中

Log.e(TAG, "執行執行緒name->" + Thread.currentThread().getName());

當onCreate執行時執行的log:

執行執行緒name->main

當JsObject類中的方法執行時的log:

執行執行緒name->WebViewCoreThread

很明顯,子執行緒不允許修改主執行緒UI,所以我們想通過js呼叫java程式碼直接修改UI介面的做法是不被允許的
如果需要修改,可以通過Handler機制去解決

4.如何讓手機的返回鍵跳到上一個Web頁面
如果不對手機系統的返回鍵進行處理,那麼我們按返回鍵會直接關閉當前Activity,而不會回到上一個Web頁面
解決這個問題,我們可以重寫Activity中的onBackPressed()方法:

@Override
public void onBackPressed() {
    super.onBackPressed();
    if (mWebView.canGoBack()) {
        mWebView.goBack();
    } else {
        finish();
    }
}