JavaScript调用本地API大概有4种方法:
(1)addJavascriptInterface/@JavascriptInterface
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(); });
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:
(2)自定义URL
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'; });
(3)JsAlert
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"); });
(4)搭建本地HTTP服务器
NanoHttpd 是一个轻量级的可嵌入的HTTP服务器。
public class APIHttpServer extends NanoHTTPD { public APIHttpServer() { super(4000); } @Override public Response serve(String uri, Method method, Mapheaders, Map params, Map 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
还有一种方法,通过URL的变更也能够实现,但是存在安全问题,基本不使用!
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); // 截取参数处理 } } }
参考:
http://www.buildinsider.net/mobile/bookhtml5hybrid