js调用java方法
这事得从一次线上事故说起。调用去年我们有个H5活动页,调用用户点击按钮后要调起原生相机,调用结果在安卓低端机上直接白屏。调用查了半天,调用发现是调用JS桥接层超时——前端等Java方法返回结果,等了3秒没响应就崩了。调用后来改成回调通知模式,调用问题才解决。调用
JS和Java的调用通信,本质上就两条路:要么用系统提供的调用桥接接口,要么自己搭一套协议。调用Android的调用WebView有`addJavascriptInterface`,这是调用最直接的方案。你把Java对象暴露给JS,调用JS直接调它的方法,跟调自己家的函数一样。但坑也在这——暴露的方法必须是public,还得加`@JavascriptInterface`注解,不然4.2以上系统直接给你静默屏蔽。
具体长这样:
webView.addJavascriptInterface(new NativeBridge(), "Android"); 然后在Java类里:
public class NativeBridge { @JavascriptInterface public void showToast(String msg) { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } } JS这边调用就一行:
window.Android.showToast("你好"); 看着挺简单对吧?但这里有个致命细节:**JS调用Java方法时,Java代码跑在WebView的主线程上**。你要是敢在方法里做网络请求或者读写大文件,UI直接卡死,用户看到的画面就像PPT。我之前接过一个需求,要在JS里调Java去拿本地数据库的数据,结果数据量一大,WebView直接无响应,最后只能把查询扔到子线程,通过回调把结果传回JS。
回调怎么传?得靠`WebView.loadUrl`或者`evaluateJavascript`。Java方法执行完,拼一段JS代码执行回去:
mWebView.post(new Runnable() { @Override public void run() { mWebView.loadUrl("javascript:callbackFromNative(" + result + ")"); } }); 这种方式有个毛病——`loadUrl`会刷新页面,频繁调用性能很差。后来我用`evaluateJavascript`替代,Android 4.4以上支持,效率高不少,还能拿返回值。
再说说安全。`addJavascriptInterface`在Android 4.2之前有个著名漏洞,恶意网页能通过反射调用Java任意方法。现在系统虽然加了注解限制,但你要是把整个Activity对象暴露出去,等于给黑客留了个后门。我见过有人图省事直接`addJavascriptInterface(this, "Android")`,Activity里所有public方法全暴露了,包括一些内部工具方法,这习惯得改。
还有个场景是JS主动拉数据,不是调方法,而是调方法拿返回值。`evaluateJavascript`支持同步返回,但只能在UI线程调用,而且返回值只能处理基本类型和JSON字符串。你要传个复杂对象,得自己转成JSON串,JS那边再parse。
反过来,Java调JS就简单多了。`loadUrl`和`evaluateJavascript`都行,后者还能拿到JS执行结果。但注意别在页面还没加载完就调,不然会没反应。得监听`onPageFinished`,或者用`WebViewClient`的`onProgressChanged`判断加载进度。
最后提一嘴跨平台方案。现在很多团队用JsBridge或者DSBridge这类开源库,它们把通信封装成统一接口,自动处理线程切换和参数序列化。但别盲目上框架——如果你的业务只有两三个调用点,自己写几十行代码就够,引入框架反而增加维护成本。我之前维护过一个老项目,桥接层自己写的,虽然丑但稳定跑了三年没出过问题。
说到底,JS调Java就是个消息传递问题。想清楚数据流方向,控制好线程,处理好生命周期,比用什么框架都重要。遇到白屏先看Logcat,多半是主线程卡死或者注解没加,别急着重构。













