成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Android中Java和JS的交互

lx1036 / 2008人閱讀

摘要:中和的交互方式在進(jìn)行交互之前需要我們對(duì)進(jìn)行設(shè)置開啟對(duì)的支持。定義和相關(guān)的交互類和方法,對(duì)于方法通過(guò)注解進(jìn)行標(biāo)注。向添加該,同時(shí)為其指定一個(gè)名稱,該名稱將會(huì)在文件中使用。傳遞的數(shù)據(jù)中有一個(gè)端口號(hào),通過(guò)這個(gè)端口號(hào)作為標(biāo)示,來(lái)調(diào)用相應(yīng)的方法。

隨著H5性能的提升,在我們移動(dòng)應(yīng)用開發(fā)的過(guò)程中,我們會(huì)越來(lái)越多的在我們的App頁(yè)面內(nèi)嵌入H5頁(yè)面,使得App變的更加動(dòng)態(tài)靈活。而H5頁(yè)面往往并不是獨(dú)立,很多時(shí)候需要和native進(jìn)行交互,調(diào)用native的一些方法,或者Web中的一些方法被native所調(diào)用。

現(xiàn)在有很多開源的解決方案,比如JSBridge,可以很方便的讓我們進(jìn)行web頁(yè)面和native的交互,其實(shí)現(xiàn)是在WebView原有提供的Web和Native通信基礎(chǔ)上做了封裝,由于最近接手工作中用到了JSBridge,借此機(jī)會(huì)學(xué)習(xí)了一下,本文先從系統(tǒng)提供的一些WebView和Native交互接口講起,然后對(duì)于一個(gè)簡(jiǎn)單的開源JSBridge 的剖析。

WebView的使用
 WebView  webView= (WebView) findViewById(R.id.webview);
 webView.loadUrl("file:///android_asset/index.html");

我們可以通過(guò)將WebView內(nèi)嵌在App界面中,來(lái)裝載網(wǎng)頁(yè),通過(guò)loadUrl,給予一個(gè)本地或者遠(yuǎn)程的地址,程序執(zhí)行即可裝載出我們的界面。這里代碼演示的是在assets文件下一個(gè)index.html文件,然后通過(guò)我們的Webview裝載的。

Android中 JS和Java的交互方式

在進(jìn)行交互之前需要我們對(duì)WebView進(jìn)行設(shè)置開啟對(duì)JS的支持。

WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);

Java調(diào)用JS

通過(guò)WebView的loadUrl()

通過(guò)WebView的evaluateJavascript()

JS調(diào)用Java

通過(guò)WebView的JavascriptInterface

通過(guò)WebViewClient.shouldOverrideUrlLoading(),攔截加載信息

通過(guò)WebChromeClient.onConsoleMessage(),攔截控制臺(tái)信息

通過(guò)WebChromeClient.onJsPrompt(),onJsAlert()、onJsConfirm()攔截Web相應(yīng)彈框的事件

Java調(diào)用JS

在Java中調(diào)用JS的代碼有兩種方式,分別為通過(guò)loadurl和通過(guò)evaluateJavascript.

首先定義了一個(gè)html文件,然后將其放置在asset目錄下。通過(guò)WebView loadUrl裝載。




    我的頁(yè)面
     
    



    

Android Java JS 交互測(cè)試

通過(guò)loadUrl來(lái)調(diào)用JS方法

mWebView.post(new Runnable() {
            @Override
            public void run() {
                mWebView.loadUrl("javascript:alertTest()");
            }
        });

通過(guò)evaluateJavascript來(lái)調(diào)用JS方法

通過(guò)該方法,我們還可以得到JS方法的返回值,來(lái)進(jìn)行值的展示。

mWebView.evaluateJavascript("javascript:alertTest()", new ValueCallback() {
            @Override
            public void onReceiveValue(String value) {
                Toast.makeText(WebViewActivity.this, value, Toast.LENGTH_SHORT).show();
            }
        });

這兩個(gè)方法在開始調(diào)用的時(shí)候,出現(xiàn)的問(wèn)題是報(bào)出錯(cuò)誤信息,錯(cuò)誤信息表示調(diào)用的JS方法未被定義,問(wèn)題原因是因?yàn)樵趏ncreate或者onResume方法中調(diào)用的時(shí)候,其JavaScript文件未被完全加載完成,因此出現(xiàn)了該問(wèn)題,可以通過(guò)監(jiān)聽WebView的裝載事件延遲調(diào)用來(lái)解決該問(wèn)題。

JS調(diào)用Java

JavascriptInterface

該種方式由于存在著缺陷,后來(lái)被棄用。具體問(wèn)題將在下面介紹。這里先講一下其使用的方式。

1.定義和JS相關(guān)的交互類和方法,對(duì)于方法通過(guò)注解進(jìn)行標(biāo)注。

public class JSTest {

  private Context mContext;

  public JSTest(Context context) {
      mContext = context;
   }

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

2.向WebView添加該JavaScriptInterface,同時(shí)為其指定一個(gè)名稱,該名稱將會(huì)在JS文件中使用。

 mWebView.addJavascriptInterface(new JsTest(context), "JsTest");

JS文件

 function showToast() {
        JsTest.showToast("來(lái)自Web調(diào)用");
 }

shouldOverrideUrlLoading

在WebViewClient中有一個(gè)方法shouldOverrideUrlLoading,該方法在每次有新的鏈接跳轉(zhuǎn)的時(shí)候,該函數(shù)都會(huì)被回調(diào),同時(shí)傳遞該次跳轉(zhuǎn)的url,所以我們可以根據(jù)自己的需求制定一個(gè)url的規(guī)則,在這里對(duì)于url進(jìn)行判斷,如果是我們協(xié)議內(nèi)的,則進(jìn)行攔截,解析我們的協(xié)議,然后進(jìn)行相應(yīng)的方法調(diào)用。

onConsoleMessage()
在WebChromeClient中,有一個(gè)函數(shù)回調(diào),當(dāng)我們有console消息的時(shí)候,該函數(shù)就會(huì)被回調(diào)到。因此,我們可以自己制定規(guī)則,然后觸發(fā)console消息,這個(gè)時(shí)候該函數(shù)就會(huì)被回調(diào),回調(diào)之后,根據(jù)我們的規(guī)則進(jìn)行解析,然后調(diào)用我們本地相應(yīng)的方法。

1.在WebChromeClient中定義相應(yīng)回調(diào)方法的攔截處理

 @Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
        String msg = consoleMessage.message();
        if ("showToast".equals(msg)) {
            Toast.makeText(mContext, "來(lái)自Web Toast測(cè)試", Toast.LENGTH_SHORT).show();
        }
        return super.onConsoleMessage(consoleMessage);
    }

JS文件,向控制臺(tái)輸出信息。

 function consoleTest() {
     console.log("showToast");
 }

onJsPrompt,onJsConfirm, onJsAlert

除了onConsoleMessage的回調(diào)之外,WebChromeClient還提供了onJsConfirm,onJsAlert,onJSPrompt等回調(diào),這些在web端有相應(yīng)的操作的時(shí)候,都會(huì)被回調(diào)到。對(duì)于其攔截,要對(duì)其中的result做判斷和處理,返回值為true,則表示不再執(zhí)行,如果返回值不是true,則會(huì)網(wǎng)頁(yè)上的操作繼續(xù)被執(zhí)行。我們可以通過(guò)該種消息的回調(diào)來(lái)傳遞一些信息,通過(guò)這個(gè)信息來(lái)實(shí)現(xiàn)JS和Java的交互。

對(duì)于三種回調(diào)的方式,onJsPrompt可以傳遞一個(gè)任意的值給web,而JsConfirm只能傳遞是否,onJsAlert則不能夠傳遞值,因此為了實(shí)現(xiàn)JS和Java的互相調(diào)用,onJsPrompt使用是最方便的。

1.JS文件

function promptTest {
       var result = prompt("js://test?arg1=device");
      alert("device: " + result);
}

2.WebChromeClient中做相應(yīng)的攔截,這里直接按照傳遞的數(shù)據(jù)做比對(duì)處理,沒(méi)有做協(xié)議的約束和解析,然后返回一個(gè)當(dāng)前的設(shè)備類型。

 @Override
 public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
     if ("js://test?arg1=device".equals(message)) {
        result.confirm("模擬器");
     }
      return true;
}

JSBridge的實(shí)現(xiàn)

上面分析了JS和Java的交互的方式,但是如果只是通過(guò)上述的方式進(jìn)行通信,必然會(huì)使得代碼比較臃腫,也難以維護(hù),因此就出現(xiàn)了各種框架來(lái)對(duì)其進(jìn)行封裝。這里給出一個(gè)簡(jiǎn)單地通信包裝。

我們的需求是實(shí)現(xiàn)JS和Java的互相調(diào)用,比如JS調(diào)用了Java的方法,執(zhí)行完成之后,能夠?qū)⒔Y(jié)果返回,同時(shí)使用返回的結(jié)果作為JS方法的參數(shù),執(zhí)行相應(yīng)的JS方法。這里采取的通信方式是通過(guò)對(duì)onJsPrompt的攔截解析,然后通過(guò)loadUrl的方式執(zhí)行JS方法。這里分析的是一個(gè)簡(jiǎn)單開源JSBridge的實(shí)現(xiàn)。

Java方法處理

對(duì)于JS可能會(huì)調(diào)用到的Java方法,進(jìn)行集中管理,對(duì)于每一個(gè)類,可以自定義名稱,方便在JS中的調(diào)用??赡軙?huì)被調(diào)用到的Java方法,都要進(jìn)行注冊(cè)。

public class JSBridge {

    private static Map> exposedMethods = new HashMap<>();

    public static void register(String exposedName, Class clazz) {
        if (!exposedMethods.containsKey(exposedName)) {
            try {
                exposedMethods.put(exposedName, getAllMethod(clazz));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
      ....
}

除此之外,還提供了一個(gè)調(diào)用函數(shù),這個(gè)函數(shù)主要是對(duì)傳遞的數(shù)據(jù)根據(jù)我們制定的協(xié)議進(jìn)行解析,然后從注冊(cè)的函數(shù)中找到所要調(diào)用的函數(shù),執(zhí)行Java函數(shù)。

public static String invokeNative(WebView webView, String uriString) {
      //協(xié)議解析
        String methodName = "";
        String className = "";
        String param = "{}";
        String port = "";
        if (!TextUtils.isEmpty(uriString) && uriString.startsWith("JSBridge")) {
            Uri uri = Uri.parse(uriString);
            className = uri.getHost();
            param = uri.getQuery();
            port = uri.getPort() + "";
            String path = uri.getPath();
            if (!TextUtils.isEmpty(path)) {
                methodName = path.replace("/", "");
            }
        }

      //查找方法,執(zhí)行相應(yīng)函數(shù)
        if (exposedMethods.containsKey(className)) {
            HashMap methodHashMap = exposedMethods.get(className);

            if (methodHashMap != null && methodHashMap.size() != 0 && methodHashMap.containsKey(methodName)) {
                Method method = methodHashMap.get(methodName);
                if (method != null) {
                    try {
                        method.invoke(null, webView, new JSONObject(param), new Callback(webView, port));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

從上面函數(shù)執(zhí)行的語(yǔ)句中,可以看到起傳遞的參數(shù)有WebView,JSonObject和一個(gè)Callback。

method.invoke(null, webView, new JSONObject(param), new Callback(webView, port));

這里用來(lái)給JS調(diào)用的Java方法,傳遞的值都是JsonObject的形式,Callback則是回調(diào)相應(yīng)的JS方法,當(dāng)我們的Java方法執(zhí)行完成之后,如果我們需要調(diào)用相應(yīng)的JS方法,我們可以通過(guò)callback提供的apply方法來(lái)傳遞一些數(shù)據(jù)。

    public static void showToast(WebView webView, JSONObject param, final Callback callback) {
        String message = param.optString("msg");
        Toast.makeText(webView.getContext(), message, Toast.LENGTH_SHORT).show();
        if (null != callback) {
            try {
                JSONObject object = new JSONObject();
                object.put("key", "value");
                object.put("key1", "value1");
                callback.apply(getJSONObject(0, "ok", object));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

這里callback的apply方法的實(shí)現(xiàn)。

private static final String CALLBACK_JS_FORMAT = "javascript:JSBridge.onFinish("%s", %s);";

public void apply(JSONObject jsonObject) {
     final String execJs = String.format(CALLBACK_JS_FORMAT, mPort, String.valueOf(jsonObject));
     if (mWebViewRef != null && mWebViewRef.get() != null) {
         mHandler.post(new Runnable() {
           @Override
            public void run() {
               mWebViewRef.get().loadUrl(execJs);
           }
         });
     }
  }

這里將需要傳回的數(shù)據(jù),傳遞給JSBridge中的onFinish方法。傳遞的數(shù)據(jù)中有一個(gè)端口號(hào),通過(guò)這個(gè)端口號(hào)作為標(biāo)示,來(lái)調(diào)用相應(yīng)的方法。

JS方法處理

callbacks: {},
call: function (obj, method, params, callback) {
      var port = Util.getPort();
      this.callbacks[port] = callback;
      var uri=Util.getUri(obj,method,params,port);
      window.prompt(uri, "");
},
onFinish: function (port, jsonObj){
     var callback = this.callbacks[port];
     callback && callback(jsonObj);
     delete this.callbacks[port];
},

JS文件提供了兩個(gè)方法,一個(gè)是call一個(gè)是finish,分別是web中被調(diào)用,另一個(gè)是在native中將會(huì)被調(diào)用,每一個(gè)web中調(diào)用我們native方法的時(shí)候,都會(huì)調(diào)用js文件中的oncall方法,同時(shí)也會(huì)傳遞一個(gè)函數(shù)作為回調(diào),js文件中會(huì)為該次調(diào)用隨機(jī)生成一個(gè)端口號(hào),同時(shí)將其回調(diào)保存在一個(gè)內(nèi)部列表callbacks中,根據(jù)協(xié)議規(guī)則,通過(guò)window.promt的方式將相應(yīng)的調(diào)用傳遞下去。

public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
    result.confirm(JSBridge.invokeNative(view, message));
    return true;
}

在WebChromeClient的onJsPrompt方法中便可以得到相應(yīng)的信息,通過(guò)JSBridge方法對(duì)回傳數(shù)據(jù)進(jìn)行相應(yīng)的調(diào)用。

JS中調(diào)用Java方法的方式

JSBridge.call("bridge","showToast",{"msg":"Hello JSBridge"},function(res){alert(JSON.stringify(res))})

第一,二個(gè)參數(shù)表示調(diào)用的Java的方法,第三個(gè)參數(shù)為傳遞的參數(shù)。第四個(gè)參數(shù)為設(shè)置的回調(diào)函數(shù)。

至此,一個(gè)簡(jiǎn)單的JSBridge實(shí)現(xiàn)了,JS只需要通過(guò)call方法傳遞相應(yīng)的參數(shù)即可調(diào)用Java方法,Java只需要將待調(diào)用方法進(jìn)行注冊(cè)即可。

交互中的安全漏洞問(wèn)題

進(jìn)幾年和WebView遠(yuǎn)程代碼執(zhí)行相關(guān)的漏洞主要有CVE-2012-6336,CVE-2014-1939,CVE-2014-7224, 這些漏洞中最核心的漏洞是CVE-2012-6336,另外兩個(gè)CVE只是發(fā)現(xiàn)了幾個(gè)默認(rèn)存在的接口。

CVE-2012-6636

Android API 16.0及之前的版本中存在安全漏洞,該漏洞源于程序沒(méi)有正確限制使用WebView.addJavascriptInterface方法。遠(yuǎn)程攻擊者可通過(guò)使用Java Reflection API利用該漏洞執(zhí)行任意Java對(duì)象的方法

Google Android <= 4.1.2 (API level 16) 受到此漏洞的影響。

CVE-2014-1939

java/android/webkit/BrowserFrame.java 使用addJavascriptInterface API并創(chuàng)建了SearchBoxImpl類的對(duì)象。攻擊者可通過(guò)訪問(wèn)searchBoxJavaBridge_接口利用該漏洞執(zhí)行任意Java代碼。

Google Android <= 4.3.1 受到此漏洞的影響

CVE-2014-7224

香港理工大學(xué)的研究人員發(fā)現(xiàn)當(dāng)系統(tǒng)輔助功能中的任意一項(xiàng)服務(wù)被開啟后,所有由系統(tǒng)提供的WebView都會(huì)被加入兩個(gè)JS objects,分別為是accessibility和accessibilityTraversal。惡意攻擊者就可以使用accessibility和accessibilityTraversal這兩個(gè)Java Bridge來(lái)執(zhí)行遠(yuǎn)程攻擊代碼.

Google Android < 4.4 受到此漏洞的影響。

對(duì)于上述漏洞,其攻擊原理為得到了Java對(duì)象,通過(guò)反射的方式來(lái)執(zhí)行自己的惡意代碼。

解決方案

1.移除掉原有提供的JavaScript接口

private static final void removeJavascriptInterfaces11(WebView webView) {
     try {
       webView.removeJavascriptInterface("searchBoxJavaBridge_");

       webView.removeJavascriptInterface("accessibility");            

        webView.removeJavascriptInterface("accessibilityTraversal");
      } catch (Throwable tr) {
            tr.printStackTrace();
     }
}    

2.升級(jí)系統(tǒng)API level 17后,只有顯示添加 @JavascriptInterface的方法才能被JavaScript調(diào)用,這樣反射就失去作用了。但對(duì)于更低版本則還是會(huì)存在,考慮采用其它方案,例如JSBridge實(shí)現(xiàn)交互。

參考資料

JsBridge 實(shí)現(xiàn) JavaScript 和 Java 的互相調(diào)用

Android:你要的WebView與 JS 交互方式 都在這里

Android WebView遠(yuǎn)程執(zhí)行代碼漏洞淺析

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88944.html

相關(guān)文章

  • AndroidJavaScript 交互 支持數(shù)據(jù)類型

    摘要:若是自定義類型,則接收不到正確數(shù)據(jù)可以通過(guò)原生方法的返回值獲取內(nèi)置類型的參數(shù),如若需要互傳復(fù)雜類型的對(duì)象,雙方須以對(duì)媒介,以各自語(yǔ)言的工具類進(jìn)行相互轉(zhuǎn)化參見 showImg(https://segmentfault.com/img/remote/1460000018767532); 在Android混合開發(fā)中,WebView是中間件,承擔(dān)著渲染網(wǎng)頁(yè)的職責(zé)。 WebView的使用教程參見...

    aaron 評(píng)論0 收藏0
  • AndroidJavaScript 交互 支持數(shù)據(jù)類型

    摘要:若是自定義類型,則接收不到正確數(shù)據(jù)可以通過(guò)原生方法的返回值獲取內(nèi)置類型的參數(shù),如若需要互傳復(fù)雜類型的對(duì)象,雙方須以對(duì)媒介,以各自語(yǔ)言的工具類進(jìn)行相互轉(zhuǎn)化參見 showImg(https://segmentfault.com/img/remote/1460000018767532); 在Android混合開發(fā)中,WebView是中間件,承擔(dān)著渲染網(wǎng)頁(yè)的職責(zé)。 WebView的使用教程參見...

    amc 評(píng)論0 收藏0
  • Cordova插件JavaScript代碼與Java交互細(xì)節(jié)介紹

    摘要:那么本身是代碼,它是怎么調(diào)用到的實(shí)現(xiàn)的本文就會(huì)介紹這個(gè)細(xì)節(jié)。下圖是離線存儲(chǔ)插件的實(shí)現(xiàn)代碼的一部分。待執(zhí)行的的實(shí)現(xiàn)類名稱。傳遞給的參數(shù)數(shù)組。 在Cordova官網(wǎng)中有這么一張架構(gòu)圖:大家看右下角藍(lán)色的矩形框Custom Plugin——自定義插件。意思就是如果您用Cordova打包Mobile應(yīng)用時(shí),發(fā)現(xiàn)您的移動(dòng)應(yīng)用里需要使用一些功能,這些功能用普通的JavaScript無(wú)法實(shí)現(xiàn),而是需...

    MycLambert 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<