为什么手机浏览器或者WebView中不能执行JavaScript调用本地API,而在HTML5混合式应用中却能执行?
JavaScript调用本地API大概有4种方法:
[b](1)addJavascriptInterface/@JavascriptInterface[/b]
Android的WebView类的标准接口。
addJavascriptInterface()方法的第一个参数就是要暴露给JavaScript的Class对象,第二个参数是可以调动该Class方法的JavaScript的全局变量。
但是addJavascriptInterface()方法存在安全隐患,在JavaScript中可以反射调用到Class的任意属性,比如以下代码能够取到Activity的package名。
[img]http://dl2.iteye.com/upload/attachment/0095/9619/ee1f6ed4-53a2-342d-86fa-5e65da202e70.png[/img]
Android 4.2 Jelly Bean以后版本,只有标示了@JavascriptInterface的方法JavaScript才能调到。
Android 4.2以前版本看到的是:"doSomething2@MyCustomHander",而Android 4.2以后版本看到的是:"doSomething@MyCustomHander"。
比如Android 4.1:
[img]http://dl2.iteye.com/upload/attachment/0095/9621/6b9d72a0-a9bd-398b-9018-1ecb43ea5d0f.png[/img]
[b](2)自定义URL[/b]
[img]http://dl2.iteye.com/upload/attachment/0095/9623/9f068fbf-6cf4-3913-83b6-97dee83619a3.png[/img]
[b](3)JsAlert[/b]
[img]http://dl2.iteye.com/upload/attachment/0095/9625/a7217c91-7717-3d57-be49-b92ecbe42070.png[/img]
[b](4)搭建本地HTTP服务器[/b]
[url=https://github.com/NanoHttpd/nanohttpd]NanoHttpd[/url] 是一个轻量级的可嵌入的HTTP服务器。
AndroidManifest.xml
[img]http://dl2.iteye.com/upload/attachment/0095/9627/64c0f659-d220-3428-a8ca-0d5fd6639441.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0095/9629/4f2f6de5-ce71-35b4-8afa-6d8a57de425e.png[/img]
[color=red]还有一种方法,通过URL的变更也能够实现,但是存在安全问题,基本不使用![/color]
参考:
[url=http://www.buildinsider.net/mobile/bookhtml5hybrid]http://www.buildinsider.net/mobile/bookhtml5hybrid[/url]
JavaScript调用本地API大概有4种方法:
[b](1)addJavascriptInterface/@JavascriptInterface[/b]
Android的WebView类的标准接口。
webView.addJavascriptInterface(new JSHandler(this), "Bridge");
class JSHandler {
public Context context;
public JSHandler(Context c) {
this.context = c;
}
public void doSomething() {
Log.d("JSHandler", "doSomething@JSHandler");
Toast.makeText(this.context, "doSomething@JSHandler", Toast.LENGTH_LONG).show();
}
}addJavascriptInterface()方法的第一个参数就是要暴露给JavaScript的Class对象,第二个参数是可以调动该Class方法的JavaScript的全局变量。
$(function() {
Bridge.doSomething();
});但是addJavascriptInterface()方法存在安全隐患,在JavaScript中可以反射调用到Class的任意属性,比如以下代码能够取到Activity的package名。
$(function() {
var klass = Bridge.getClass();
var field = klass.getDeclaredField('context');
field.setAccessible(true);
var context = field.get(Bridge);
document.getElementById('res').innerHTML = context.getPackageName();
});[img]http://dl2.iteye.com/upload/attachment/0095/9619/ee1f6ed4-53a2-342d-86fa-5e65da202e70.png[/img]
Android 4.2 Jelly Bean以后版本,只有标示了@JavascriptInterface的方法JavaScript才能调到。
webView.addJavascriptInterface(new MyCustomHander(this), "Bridge");
class MyCustomHander {
public Context context;
public MyCustomHander(Context c) {
this.context = c;
}
@JavascriptInterface
public void doSomething() {
Log.d("MyCustomHander", "doSomething@MyCustomHander");
Toast.makeText(this.context, "doSomething@MyCustomHander", Toast.LENGTH_LONG).show();
}
public void doSomething2() {
Log.d("MyCustomHander", "doSomething2@MyCustomHander");
Toast.makeText(this.context, "doSomething2@MyCustomHander", Toast.LENGTH_LONG).show();
}
}Android 4.2以前版本看到的是:"doSomething2@MyCustomHander",而Android 4.2以后版本看到的是:"doSomething@MyCustomHander"。
比如Android 4.1:
[img]http://dl2.iteye.com/upload/attachment/0095/9621/6b9d72a0-a9bd-398b-9018-1ecb43ea5d0f.png[/img]
[b](2)自定义URL[/b]
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("apicall://")) {
Log.d("MyWebViewClient", "doSomething@WebViewClient: " + url);
Toast.makeText(getBaseContext(), "doSomething@WebViewClient: " + url, Toast.LENGTH_LONG).show();
return true;
}
return false;
}
});$(function() {
window.location = 'apicall:////some_api_name/exec?a=1&b=2';
});[img]http://dl2.iteye.com/upload/attachment/0095/9623/9f068fbf-6cf4-3913-83b6-97dee83619a3.png[/img]
[b](3)JsAlert[/b]
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url,
String message, JsResult result) {
Log.d("MyWebChromeClient", "doSomething@WebChromeClient: " + message);
Toast.makeText(getBaseContext(), "doSomething@WebChromeClient: " + message, Toast.LENGTH_LONG).show();
// return super.onJsAlert(view, url, message, result);
return true;
}
});$(function() {
alert("123");
});[img]http://dl2.iteye.com/upload/attachment/0095/9625/a7217c91-7717-3d57-be49-b92ecbe42070.png[/img]
[b](4)搭建本地HTTP服务器[/b]
[url=https://github.com/NanoHttpd/nanohttpd]NanoHttpd[/url] 是一个轻量级的可嵌入的HTTP服务器。
public class APIHttpServer extends NanoHTTPD {
public APIHttpServer() {
super(4000);
}
@Override
public Response serve(String uri, Method method,
Map<String, String> headers, Map<String, String> params,
Map<String, String> files) {
String data = "uri=" + uri + ", params=" + params;
Log.d("APIHttpServer", "doSomething@APIHttpServer: " + data);
return new NanoHTTPD.Response(Status.OK, "application/json", "{msg:'nano'}");
}
}
APIHttpServer server = new APIHttpServer();
server.start();$(function() {
$.getJSON("http://localhost:4000/some_api_name?a=1&b=2&c=3");
});AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>[img]http://dl2.iteye.com/upload/attachment/0095/9627/64c0f659-d220-3428-a8ca-0d5fd6639441.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0095/9629/4f2f6de5-ce71-35b4-8afa-6d8a57de425e.png[/img]
[color=red]还有一种方法,通过URL的变更也能够实现,但是存在安全问题,基本不使用![/color]
window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;public class MyWebViewClient extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http://cdv_exec/")) {
handleExecUrl(url); // 截取参数处理
}
}
}参考:
[url=http://www.buildinsider.net/mobile/bookhtml5hybrid]http://www.buildinsider.net/mobile/bookhtml5hybrid[/url]
本文介绍了在HTML5混合应用中JavaScript如何调用本地API的四种主要方式:使用addJavascriptInterface/@JavascriptInterface、自定义URL、JsAlert及搭建本地HTTP服务器,并强调了不同Android版本的安全性差异。
5123

被折叠的 条评论
为什么被折叠?



