Android與JS交互

現(xiàn)在越來越多的App是混合開發(fā)糟描,很需要原生與網頁交互渗磅,下面介紹如何交互以及有可能出現(xiàn)的坑。

Java調用JS方法

對于Android調用JS方法的方式有2種:

  • 通過WebViewloadUrl()
  • 通過WebViewevaluateJavascript()

方式一:loadUrl()

  • 第一步:設置與Js交互的權限
WebSettings webSettings = mWebView.getSettings();
// 設置與Js交互的權限
webSettings.setJavaScriptEnabled(true);
  • 第二步:設置加載網頁
mWebview.loadUrl("加載的網頁");
  • 第三步:調用JS方法
    通過使用mWebview.loadUrl 調用“javascript:" + 方法
    如果JS這樣定義的
<script>
   function callJS(){
      alert("Android調用了JS的無參callJS方法");
   }
   function callJSParameter(message){
       alert("Android調用JS的有參callJS方法的圆,參數(shù)為"+message);
   }
</script>

在Java代碼中

//調用無參
mWebView.loadUrl("javascript:callJS()");
//調用有參
mWebView.loadUrl("javascript:callJSParameter('測試')");

方式一注意事項(也就是坑)

  1. 調用JS方法時一定要在onPageFinished()回調之后才能調用力九,否則不會調用。

onPageFinished()屬于WebViewClient類的方法塞赂,主要在頁面加載結束時調用

  1. 調用JS方法時一定要在主線程泪勒,否則會崩潰異常。
java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'Thread-18022'. All WebView methods must be called on the same thread.

如果非要在子線程調用减途,也要將其轉換到主線程中去:

mWebView.post(new Runnable() {
                    @Override
                    public void run() {
                        mWebView.loadUrl("javascript:callJS()");
                    }
 });
  1. 調用的帶參數(shù)的JS方法時酣藻,參數(shù)類型如果為String ,切記使用單引號( ') 包裹鳍置;如果為數(shù)組類型則不用辽剧,如:javascript:callJs([01, 02, 03]);如果為其他復雜類型則可以轉換為 Json 字符串的形式傳遞税产。

方式二:evaluateJavascript()

*執(zhí)行該方法不會使頁面刷新怕轿,而第一種方法(loadUrl )的執(zhí)行則會,但是Android 4.4 后才可使用辟拷,兼容性要求偏高撞羽。
JS代碼

function callJSReturn(message){
            return message;
        }

Java代碼

mWebView.evaluateJavascript("javascript:callJSReturn('方式二調用JS')", new ValueCallback<String>() {
                    @Override
                    public void onReceiveValue(String value) {
                        //此處為 js 返回的結果
                        Toast.makeText(MainActivity.this, value, Toast.LENGTH_SHORT).show();
                    }
                });

兩種方式對比

調用方式 優(yōu)點 缺點 使用場景
loadUrl() 兼容性好 獲取返回值麻煩 不需要獲取返回值
evaluateJavascript() 只能從4.4(18)以上使用 獲取返回值簡單 4.4(18)以上

JS調用Java

JS調用Java的方式有三種

  • 通過WebViewaddJavascriptInterface()
  • 通過WebViewClientshouldOverrideUrlLoading()方法回調攔截 url
  • 通過WebChromeClientonJsAlert()onJsConfirm()衫冻、onJsPrompt()方法回調攔截JS對話框alert()诀紊、confirm()prompt()消息

方式一:addJavascriptInterface()

JS代碼

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>LYJ</title>
    <script>
         function testCallAndroid(){
            test.callAndroid("JS調用了Android方法");
         }
    </script>
</head>
<body>
    <button type="button" id="button1" onclick="testCallAndroid()">點擊按鈕通過方式一調用Android方法</button>
</body>
</html>
  • 第一步:定義一個JS調用的方法
@JavascriptInterface
    public void callAndroid(String message){
        Toast.makeText(CallAndroidActivity.this,message,Toast.LENGTH_SHORT).show();
    }

1.必須要有@JavascriptInterface隅俘,在 Android4.2(17) 及更高版本的系統(tǒng)中邻奠,任何暴露給 Js 訪問的 Java 接口都需要添加這個注解,否則會報異常:Uncaught TypeError: Object [object Object] has no method ‘XXX’为居。系統(tǒng)在之前的版本中有漏洞碌宴,Js 可以通過反射的方式訪問注入 WebView 中的 Java 對象的 public 類型 field 和 method,從而隨意修改宿主程序蒙畴,所以為了安全增加了這個注解贰镣。想更深入的了解參考這篇文章你不知道的 Android WebView 使用漏洞
2.方法名切記要和JS調用的方法名一樣

  • 第二步將Java對象映射到JS對象
mWebView.addJavascriptInterface(this, "test");

第一個參數(shù)是寫有JS調用方法的Android對象,第二個參數(shù)是JS調用的對象名膳凝,切記跟網頁上的一致碑隆。

方式一注意事項

  • Js 調用 Java 方法時,不是在主線程 (Thread Name:main) 中運行的蹬音,而是在一個名為 JavaBridge 的線程中執(zhí)行的上煤,如果需要 Java 繼續(xù)回調 Js,千萬別在 JavascriptInterface 方法體中直接執(zhí)行 loadUrl() 方法祟绊,而是像前面一樣進行線程切換操作楼入。

方式二:WebViewClientshouldOverrideUrlLoading()方法

JS代碼

<script>
         function testTwoCallAndroid(){
            document.location = "js://webview?key1=value1&key2=value2";
         }
</script>
<body>
    <button type="button" id="button2" onclick="testTwoCallAndroid()">點擊按鈕通過方式二調用Android方法</button>
</body>

Java代碼

// 復寫WebViewClient類的shouldOverrideUrlLoading方法
        mWebView.setWebViewClient(
                new WebViewClient() {
                    @Override
                    public boolean shouldOverrideUrlLoading(WebView view, String url) {

                        // 第一步:根據(jù)協(xié)議的參數(shù),判斷是否是所需要的url
                        // 一般根據(jù)scheme(協(xié)議格式) & authority(協(xié)議名)判斷
                        //假定傳入進來的 url = "js://webview?key1=value1&key2=value2"(同時也是約定好的需要攔截的)

                        Uri uri = Uri.parse(url);
                        // 第二步:如果url的協(xié)議 = 預先約定的 js 協(xié)議牧抽,就解析往下解析參數(shù)
                        if (uri.getScheme().equals("js")) {

                            //第三步:再判斷嘉熊,如果 authority = 預先約定協(xié)議里的 webview,即代表都符合約定的協(xié)議
                            // 所以攔截url,下面JS開始調用Android需要的方法
                            if (uri.getAuthority().equals("webview")) {

                                // 第四步:
                                // 執(zhí)行JS所需要調用的邏輯
                                // 獲取協(xié)議執(zhí)行的參數(shù)
                                HashMap<String, String> params = new HashMap<>();
                                Set<String> collection = uri.getQueryParameterNames();
                                for (String key : collection){
                                    String value = uri.getQueryParameter(key);
                                    Log.d("==","key = " + key + ",value = " + value);
                                    params.put(key,value);
                                }

                            }

                            return true;
                        }
                        return super.shouldOverrideUrlLoading(view, url);
                    }
                }
        );

    }

注釋比較清楚扬舒,就按注釋的步驟走阐肤。

shouldOverrideUrlLoading(WebView view, String url)在Android 7.0(24)過時,官方改為shouldOverrideUrlLoading (WebView view,
WebResourceRequest request)讲坎。然后獲取Uri改為Uri uri = request.getUrl();孕惜。

方式三:通過WebChromeClientonJsAlert()onJsConfirm()晨炕、onJsPrompt()方法回調攔截JS對話框alert()衫画、confirm()prompt()消息

方法 作用 返回值 備注
alert() 彈出警告框 沒有 在文本加入/n可換行
confirm() 彈出確認框 兩個 返回boolean值,true代表確認瓮栗,false代表取消
prompt() 彈出輸入框 任意 點擊確認返回輸入框的值削罩,點擊取消返回null

下面的例子將用攔截 JS的輸入框(即prompt()方法)說明,其他兩種與其類似 :

JS代碼

<script>
         function testPromptCallAndroid(){
            var result=prompt("js://prompt?key1=value1&key2=value2");
            alert("方式三 " + result);
         }
</script>
<body>
    <button type="button" id="button3" onclick="testPromptCallAndroid()">點擊按鈕通過方式三調用Android方法</button>
</body>

Java代碼

mWebView.setWebChromeClient(
                new WebChromeClient() {
                    // 攔截輸入框(原理同方式2)
                    // 參數(shù)message:代表promt()的內容(不是url)
                    // 參數(shù)result:代表輸入框的返回值
                    @Override
                    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                        // 根據(jù)協(xié)議的參數(shù)费奸,判斷是否是所需要的url(原理同方式2)
                        // 一般根據(jù)scheme(協(xié)議格式) & authority(協(xié)議名)判斷(前兩個參數(shù))
                        //假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)

                        Uri uri = Uri.parse(message);
                        // 如果url的協(xié)議 = 預先約定的 js 協(xié)議
                        // 就解析往下解析參數(shù)
                        if (uri.getScheme().equals("js")) {

                            // 如果 authority  = 預先約定協(xié)議里的 webview弥激,即代表都符合約定的協(xié)議
                            // 所以攔截url,下面JS開始調用Android需要的方法
                            if (uri.getAuthority().equals("prompt")) {

                                // 執(zhí)行JS所需要調用的邏輯
                                // 可以在協(xié)議上帶有參數(shù)并傳遞到Android上
                                HashMap<String, String> params = new HashMap<>();
                                Set<String> collection = uri.getQueryParameterNames();
                                for (String key : collection) {
                                    String value = uri.getQueryParameter(key);
                                    Log.d("==", "key = " + key + ",value = " + value);
                                    params.put(key, value);
                                }
                                //參數(shù)result:代表消息框的返回值(輸入值)
                                result.confirm("js通過方式三調用了Android的方法成功");
                            }
                            return true;
                        }
                        return super.onJsPrompt(view, url, message, defaultValue, result);
                    }
                }
        );

整體上跟上一個方式相差不多。

其他

  • 如果需要回調這種需求愿阐,你只要android的異步回調中微服,使用loadUrl調用js的相關方法。
  • 如果需要給JS返回值缨历,除了方式三以外以蕴,還可以這么做
    JS代碼
function returnResult(result){
    alert("result is" + result);
}

Java代碼

mWebView.loadUrl("javascript:returnResult(" + result + ")");

參考文章

Android:你要的WebView與 JS 交互方式 都在這里了
Android WebView —— Java 與 JavaScript 交互總結
Android_其他語言交互篇——Js、C#戈二、C舒裤、C++

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市觉吭,隨后出現(xiàn)的幾起案子腾供,更是在濱河造成了極大的恐慌,老刑警劉巖鲜滩,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伴鳖,死亡現(xiàn)場離奇詭異,居然都是意外死亡徙硅,警方通過查閱死者的電腦和手機榜聂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗓蘑,“玉大人须肆,你說我怎么就攤上這事匿乃。” “怎么了豌汇?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵幢炸,是天一觀的道長。 經常有香客問我拒贱,道長宛徊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任逻澳,我火速辦了婚禮闸天,結果婚禮上,老公的妹妹穿的比我還像新娘斜做。我一直安慰自己苞氮,他們只是感情好,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布瓤逼。 她就那樣靜靜地躺著葱淳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抛姑。 梳的紋絲不亂的頭發(fā)上赞厕,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音定硝,去河邊找鬼皿桑。 笑死,一個胖子當著我的面吹牛蔬啡,可吹牛的內容都是我干的诲侮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了耿战?” 一聲冷哼從身側響起衅谷,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤患雏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡坝疼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了谆沃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钝凶。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖唁影,靈堂內的尸體忽然破棺而出耕陷,到底是詐尸還是另有隱情掂名,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布哟沫,位于F島的核電站铆隘,受9級特大地震影響,放射性物質發(fā)生泄漏南用。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一掏湾、第九天 我趴在偏房一處隱蔽的房頂上張望裹虫。 院中可真熱鬧,春花似錦融击、人聲如沸筑公。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匣屡。三九已至,卻和暖如春拇涤,著一層夾襖步出監(jiān)牢的瞬間捣作,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工鹅士, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留券躁,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓掉盅,卻偏偏與公主長得像也拜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子趾痘,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內容