Android混合编程

440 阅读3分钟

混合编程

​ Android 混合编程主要需要实现的是JSBridge,其中JSBridge是H5与Native通信的桥梁,其作用是实现H5与Native之间的双向通信。

JSBridge实现关键点

  • Java如何调用JavaScript
  • JavaScript如何调用Java
  • 方法参数以及回调如何处理
  • 通信协议的制定

Java如何调用JavaScript

js调用Android的方法有以下四种:

  1. WebView 的 JavascriptInterface
  2. WebViewClient.shouldOverrideUrlLoading()
  3. WebChromeClient.onConsoleMessage()
  4. WebChromeClient.onJsPrompt()、onJsAlert()、onJsConfirm()

我们先对此四种方案进行一个详细的描述,最后选择一个方案即可。本文章中采用了第四种方案。

为什么选择onJsPrompt

WebChromeClient.onJsPrompt()

​ 在WebView有一个方法,叫setWebChromeClient,可以设置WebChromeClient对象,而这个对象中有三个方法,分别是onJsAlert,onJsConfirm,onJsPrompt,当js调用window对象的对应的方法,即window.alert,window.confirm,window.prompt,WebChromeClient对象中的三个方法对应的就会被触发,那这三个方法到底要使用哪个呢?

  1. 这三个方法的区别,可以详见w3c JavaScript 消息框
  2. 一般来说,我们是不会使用onJsAlert的,为什么呢?因为js中alert使用的频率还是非常高的,一旦我们占用了这个通道,alert的正常使用就会受到影响,而confirm和prompt的使用频率相对alert来说,则更低一点。
  3. 那么到底是选择confirm还是prompt呢,其实confirm的使用频率也是不低的,比如你点一个链接下载一个文件,这时候如果需要弹出一个提示进行确认,点击确认就会下载,点取消便不会下载,类似这种场景还是很多的,因此不能占用confirm。
  4. 而prompt则不一样,在Android中,几乎不会使用到这个方法,就是用,也会进行自定义,所以我们完全可以使用这个方法。该方法就是弹出一个输入框,然后让你输入,输入完成后返回输入框中的内容。因此,占用prompt是再完美不过了。

image.png

方法参数以及回调处理

  1. 任何IPC通信都涉及到参数序列化的问题,同理,Java与JavaScript之间只能传递基础类型(包括基本类型和字符串),不包括其他对象或者函数。所以可以采用json格式来传递数据。
  2. 为了实现异步返回结果,所以JavaScript与Java相互调用不能直接获取返回值,只能通过回调的方式来获取返回结果。

通信协议的制定

​ 要进行正常的通信,通信协议的制定是必不可少的。我们回想一下熟悉的http请求url的组成部分。形如http://host:port/path?param=value, 我们参考http,制定JSBridge的组成部分。

image.png

具体实战

调用流程

​ 在js中,可以采用如下方法调用java方法:

var JSBridge = {

call: function(className, method, params, callback) {

​ var uri = 'jsbridge://' + className + ':' + callback + '/' + method + '?' + params;

​ window.prompt(uri, "");

}

}

// 下面会调用java中的 bridge.showToast方法

JSBridge.call('bridge', 'showToast', {'msg':'Hello JSBridge'}, function(res) {

​ alert(JSON.stringify(res))

​ });

在java中, 可以如下实现:

// 进入prompt回调

public class JSBridgeWebChromeClient extends WebChromeClient {

​ @Override

​ public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {

​ result.confirm(JSBridge.callJava(view, message));

​ return true;

​ }

}

// 调用java逻辑

public class JSBridge {

​ ...

​ public static String callJava(WebView webView, String uriString) {

​ 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("/", "");

​ }

​ }

​ // 基于上面的className、methodName和port path调用对应类的方法

​ if (exposedMethods.containsKey(className)) {

​ HashMap<String, Method> 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;

​ }

}

// 直接进入showToast函数的实现

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方法实现如下: 即通过webView.loadUrl实现java调用js的方法