1. 程式人生 > >Android與JS互動

Android與JS互動

Android與JS的方法互調

在Android的開發過程中、遇到一個新需求、那就是讓Java程式碼和Javascript程式碼進行互動、在IOS中實現起來很麻煩、而在Android中相對來說容易多了、Android對這種互動進行了很好的封裝、我們可以很簡單的用Java程式碼呼叫WebView中的js函式、也可以用WebView中的js來呼叫Android應用中的Java程式碼。 案例主要包含了: 一、Html呼叫Android方法 二、Android呼叫JS方法無引數 三、Android呼叫JS方法有引數 四、Android呼叫JS方法有引數且有返回值處理方式(Android4.4以下) 五、Android呼叫JS方法有引數且有返回值處理方式2(Android4.4以上)

一、Html呼叫Android方法

1.建立JS物件


/**
 * 新增javascriptInterface
 * 第一個引數:這裡需要一個與js對映的java物件
 * 第二個引數:該java物件被對映為js物件後在js裡面的物件名,在js中要呼叫該物件的方法就是通過這個來呼叫
 */
webView.addJavascriptInterface(this,"customName");

2.Android程式碼


@JavascriptInterface
public void showToast(String toast){
    Toast.makeText(this,toast,Toast.LENGTH_SHORT).show();
}

3.JS程式碼


//調起本地的Java方法()
function showAndroidToast(toast) {
    window.customName.showToast(toast);
}

二、Android呼叫JS方法無引數

1.Android程式碼


    isUpload=false;
   webView.post(new Runnable() {
        @Override
         public void run() {
//                webView.loadUrl("javascript:androidCallJS('"+isUpload+"')");
                   webView.loadUrl("javascript:androidCallJSNoParam()");
                                       }
                       								 });

2.JS程式碼


function androidCallJSNoParam(){
    alert("Android呼叫JS方法無引數");
}

三、Android呼叫JS方法有引數

1.Android程式碼


        isUpload=true;
        webView.post(new Runnable() {
           @Override
            public void run() {
             webView.loadUrl("javascript:androidCallJS("+isUpload+")");
                           		}

注:其實Android呼叫JS方法只需要 webView.loadUrl(“javascript:androidCallJS(”+isUpload+")"); 這一行程式碼就夠了,為什麼要放在webView.post(new Runnable(){…})這個方法中去執行呢,原因是如果直接呼叫這行程式碼,在Android4.4版本以上的手機上會出現 java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread ‘JavaBridge’. All WebView methods must be called on the same thread. 這種錯誤,大概意思是說方法被一個叫”javaBridge”的子執行緒呼叫了,所有的WebView中的方法都必須在同一個執行緒中執行.又因為webView.loadUrl()這個方法得在主執行緒中執行,所以我們要放在webView.post(new Runnable(){…})這個方法中去執行,將WebView放在主執行緒即可 參考:https://stackoverflow.com/questions/22607657/webview-methods-on-same-thread-error https://stackoverflow.com/questions/21955593/android-webview-loadurl-wont-load-another-webpage

2.JS程式碼


    function androidCallJS(isUpload){
    if(isUpload){
         alert("Android呼叫JS方法有引數");
    }
}

四、Android呼叫JS方法有引數且有返回值處理方式(Android4.4以下)

Android在4.4之前並沒有提供直接呼叫js函式並獲取值的方法,所以在此之前,常用的思路是 java呼叫js方法,js方法執行完畢,再次呼叫java程式碼將值返回。 1.Java呼叫JS程式碼


  webView.post(new Runnable() {
    @Override
    public void run() {
        webView.loadUrl("javascript:sumToJava(1,2)");
    }
});

2.JS方法執行完畢,呼叫Java程式碼將值返回


function sumToJava(number1, number2){
    var result=number1+number2;
     window.customName.onSumResult(result)
}

@JavascriptInterface
public void onSumResult(int result) {
    Log.i("TAG", "onSumResult result=" + result);
    Toast.makeText(getApplicationContext(),"result====="+result,Toast.LENGTH_SHORT).show();
}

五、Android呼叫JS方法有引數且有返回值處理方式(Android4.4以上)

Android 4.4之後使用evaluateJavascript即可。這裡展示一個簡單的互動示例 具有返回值的js方法 1.JS程式碼


function getGreetings() {
      return 1;
}

2.Java直接呼叫JS程式碼獲取返回值


@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void testEvaluateJavascript(WebView webView) {
    webView.evaluateJavascript("getGreetings()", new ValueCallback() {
        @Override
        public void onReceiveValue(String value) {
            Log.i("TAG", "onReceiveValue value=" + value);
            Toast.makeText(getApplicationContext(),"value====="+value,Toast.LENGTH_SHORT).show();
        }});
}

一些需要注意的點:

1.Alert無法彈出 應該是沒有設定WebChromeClient,按照以下程式碼設定


webView.setWebChromeClient(new WebChromeClient());

2.安全限制問題 如果只在4.2版本以上的機器出問題,那麼就是系統處於安全限制的問題了。Android文件這樣說的 Caution: If you’ve set your targetSdkVersion to 17 or higher, you must add the @JavascriptInterface annotation to any method that you want available your web page code (the method must also be public). If you do not provide the annotation, then the method will not accessible by your web page when running on Android 4.2 or higher. 中文大意為 警告:如果你的程式目標平臺是17或者是更高,你必須要在暴露給網頁可呼叫的方法(這個方法必須是公開的)加上@JavascriptInterface註釋。如果你不這樣做的話,在4.2以後的平臺上,網頁無法訪問到你的方法。 解決方法: 將targetSdkVersion設定成17或更高,引入@JavascriptInterface註釋 自己建立一個註釋介面名字為@JavascriptInterface,然後將其引入。注意這個介面不能混淆。 注,建立@JavascriptInterface程式碼


@JavascriptInterface
public void showToast(String toast){
    Toast.makeText(this,toast,Toast.LENGTH_SHORT).show();
}

3.Java程式碼不能設定為private,否則不能被JS呼叫

4.Java呼叫JS方法 All WebView methods must be called on the same thread 過濾日誌曾發現過這個問題。


E/StrictMode( 1546): java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {528712d4} called on Looper (JavaBridge, tid 121) {52b6678c}, FYI main Looper is Looper (main, tid 1) {528712d4})
E/StrictMode( 1546):   at android.webkit.WebView.checkThread(WebView.java:2063)
E/StrictMode( 1546):   at android.webkit.WebView.loadUrl(WebView.java:794)
E/StrictMode( 1546):   at com.xxx.xxxx.xxxx.xxxx.xxxxxxx$JavaScriptInterface.onCanGoBackResult(xxxx.java:96)
E/StrictMode( 1546):   at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
E/StrictMode( 1546):   at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
E/StrictMode( 1546):   at android.os.Handler.dispatchMessage(Handler.java:102)
E/StrictMode( 1546):   at android.os.Looper.loop(Looper.java:136)
E/StrictMode( 1546):   at android.os.HandlerThread.run(HandlerThread.java:61)

在js呼叫後的Java回撥執行緒並不是主執行緒。如列印日誌可驗證


lineos:false
ThreadInfo=Thread[WebViewCoreThread,5,main]

解決上述的異常,將webview操作放在主執行緒中即可。


	webView.post(new Runnable() {
    @Override
    public void run() {
        webView.loadUrl(YOUR_URL).
    }
});

5.程式碼混淆問題 如果在沒有混淆的版本執行正常,在混淆後的版本的程式碼執行錯誤,並提示Uncaught TypeError: Object [object Object] has no method,那就是你沒有做混淆例外處理。 在混淆檔案加入類似這樣的程式碼


-keep class com.example.javajsinteractiondemo$JsInteration {
    *;
}