js與native的交互
概述
目前所常用的native與js交互有兩種方式,分別為 下面提到的方法1與方法2,這兩種方式各有利弊,在4.2之前使用方法1存在安全問題,
類似與sql的注入漏洞,這是運行時虛擬機的漏洞,暫且這樣理解吧。另外無論哪種方式,都要與頁面開發人員定要協議。
sample運行效果圖
http://diycode.b0.upaiyun.com/photo/2016/3977265ae4b932319f6613dbbd2df39d.gif
方法1(webClient.loadUrl()):
mWebView = (WebView) findViewById(R.id.webview);
//支持JavaScript腳本
mWebView.getSettings().setJavaScriptEnabled(true);
// 加載html
mWebView.loadUrl("file:///android_asset/web.html");
//android為flag
mWebView.addJavascriptInterface(MainActivity.this, "android");
native中調用(帶參&不帶參)js方法實現
//調用js函數
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("javascript:javaCallJs()");
}
});
//調用js函數并攜帶參數
final String param = "'這是參數,注意這個參數的格式'";
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 傳遞參數調用
mWebView.loadUrl("javascript:javaCallJswithParam(" + param + ")");
}
});
js響應native方法實現:
<script type="text/javascript">
function javaCallJs(){
document.getElementById("content").innerHTML ="<br\>JAVA調用了JS的無參函數";
}
function javaCallJswithParam(arg){
document.getElementById("content").innerHTML =
("<br\>"+arg);
}
</script></code></pre>
<input type="button" value="調用native方法"
onclick="window.android.startFunction()"/>
<br/><br/>
<input type="button" value="調用native方法并傳參"
onclick="window.android.startFunction('native方法被js調用,并傳參')"/>
native中,被調用的方法實現
//由于安全原因 需要加 @JavascriptInterface
@JavascriptInterface
public void startFunction() {
runOnUiThread(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(MainActivity.this).setMessage("native方法觸發").show();
}
});
}
@JavascriptInterface
public void startFunction(final String text) {
runOnUiThread(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(MainActivity.this).setMessage(text).show();
}
});
}
上段備注中提到“由于安全原因 需要加@JavascriptInterface ”,是指在4.2版本之前的addjavascriptInterface接口引起的漏洞,可能導致惡意網頁通過Js方法遍歷剛剛通過addjavascriptInterface注入進來的類的所有方法從中獲取到getClass方法,然后通過反射獲取到Runtime對象,進而調用Runtime對象的exec方法執行一些操作,惡意的Js代碼如下:
function execute(args) {
for (var obj in window) {
if ("getClass" in window[obj]) {
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime")
.getMethod("getRuntime",null).invoke(null,null).exec(args);
}
}
}
在Android API Level 17(Android 4.2)之后,可以通過添加@JavascriptInterface 這個注解來避免該漏洞,在4.1及之前可以使用方法2
實現native與js之間的交互。
方法2(prompt()$onJsPrompt()/confirm()$onConfirm()/alert()$onAlert()):
繼承WebChromeClient重寫該方法的onJsPrompt()/onConfirm()/onAlert()方法,用到那個方法重寫那個就行,然后設置webView的WebChromeClient為該重寫的類
具體實現
class HarlanWebChromeClient extends WebChromeClient {
/*此處覆蓋的是javascript中的alert方法。
*當網頁需要彈出alert窗口時,會執行onJsAlert中的方法
* 網頁自身的alert方法不會被調用。
*/
@Override
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
show("onJsAlert");
result.confirm();
return true;
}
/*此處覆蓋的是javascript中的confirm方法。
*當網頁需要彈出confirm窗口時,會執行onJsConfirm中的方法
* 網頁自身的confirm方法不會被調用。
*/
@Override
public boolean onJsConfirm(WebView view, String url,
String message, JsResult result) {
show("onJsConfirm");
result.confirm();
return true;
}
/*此處覆蓋的是javascript中的confirm方法。
*當網頁需要彈出confirm窗口時,會執行onJsConfirm中的方法
* 網頁自身的confirm方法不會被調用。
*/
@Override
public boolean onJsPrompt(WebView view, String url,
String message, String defaultValue,
JsPromptResult result) {
show("onJsPrompt....");
result.confirm();
return true;
}</code></pre>
然后給webView設置該重寫的類
//設置ChromeClient
mWebView.setWebChromeClient(new HarlanWebChromeClient());
相應的在JS中的具體實現代碼如下
function cfm() {
confirm("")
}
function pmt() {
prompt("","");
}
function onAlert()
{
alert("這是網頁中的alert方法,如果重寫了mWebView的onAlert方法,該方法不會執行");
}
觸發頁面中的js函數,例如:
<p><input type="button" onclick="cfm()" value="Confirm"/></p>
<p><input type="button" onclick="pmt()" value="Prompt"/></p>
<p><input type="button" onclick="onAlert()" value="Alert"/>
實現原理就是在頁面中觸發的方法被webView中設置的WebChromeClient給攔截了,從而執行了WebChromeClient中重寫的onXxx()方法,
沒有執行頁面中相應的onXxx()方法,這是方式相對簡單,而且安全。
來自:http://www.html5cn.org/article-9808-1.html