简述
在移动应用开发中,Android和JavaScript是两个常用的技术栈。Android作为主流的移动操作系统,而JavaScript则是用于网页前端和跨平台开发的脚本语言。为了实现更好的用户体验和功能扩展,Android与JavaScript之间的通信变得至关重要。本文将介绍Android与JavaScript之间的回调通信技巧
通信基础
-
通过 WebView 进行通信
Android 的 WebView 组件提供了
evaluateJavascript
方法,该方法可以执行 JavaScript 代码并获取返回结果。我们可以利用这一特性实现 Android 和 JavaScript 之间的通信。具体实现步骤如下:- 在 Android 代码中,通过
evaluateJavascript
方法执行 JavaScript 代码。
- 在 Android 代码中,通过
-
使用 JavaScriptInterface 实现通信
我们可以使用
JavascriptInterface
接口实现JavaScript 与 Android 的通信。具体实现步骤如下:- 在 Android 代码中创建一个类,实现
JavascriptInterface
接口。 - 在该类中定义需要供 JavaScript 调用的方法,并添加
@JavascriptInterface
注解。 - 在 JavaScript 中通过
window.AndroidFunction
对象调用 Android 代码中的方法,实现通信,其中AndroidFunction是注册JavascriptInterface时指定的, 如下所示:webView.addJavascriptInterface(new Object() { @JavascriptInterface public void jsCallback(String message) { // ... } }, "AndroidFunction");
- 在 Android 代码中创建一个类,实现
Android调用JavaScript函数
-
忽略返回值
val webView = findViewById<WebView>(R.id.webView) webView.evaluateJavascript("jsFunction('message')", null)
-
获取返回值
val webView = findViewById<WebView>(R.id.webView) webView.evaluateJavascript("jsFunction('message')") { result -> Log.e("TAG", result) }
JavaScript调用Android函数
// 在JavaScript中调用Android函数,并传递参数
function callAndroidFunctionWithParameter() {
var message = "Hello from JavaScript!";
AndroidFunction.jsCallback(message);
}
在上述示例中,JavaScript函数callAndroidFunctionWithParameter()
将参数message
传递给Android函数jsCallback()
。
双向回调通信
-
Javascript传递回调给Android
上述的
AndroidFunction.jsCallback(message)
方式目前只能传递字符串,如果不做特殊处理,是无法执行回调函数的, 执行AndroidFunction.jsCallback(message)
时,在Android中获取到的是字符串,这时将回调函数转换成回调令牌,然后通过令牌执行相应的回调函数,步骤如下:-
在js中将回调函数转换成唯一令牌,然后使用map以令牌为key存储回调函数,以便让Android端根据令牌来执行回调函数
const recordCallMap = new Map<string, Function>(); // Android端调用 window.jsbridgeApiCallBack = function (callUUID: string, callbackParamsData: any) { // 通过ID获取对应的回调函数 const fun = recordCallMap.get(callUUID); // 执行回调 `callbackParamsData`回调函数的参数 可有可无 fun && fun(callbackParamsData); // 执行完毕后释放资源 recordCallMap.delete(callUUID); } function getUuid() { // 生成唯一ID return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/\[xy]/g, function (c) { var r = (Math.random() \* 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } // 统一处理调用Android的方法 export function androidCall(funname: string, funData: string, fun: Function) { if (!AndroidFunction) { return; } const dataObj = { funName: funname, funData: funData } if (typeof fun === "function") { const funId = getUuid(); Object.assign(dataObj, { funId }); recordCallMap.set(funId, fun); } AndroidFunction.jsCall(JSON.stringify(dataObj)) }
-
在Android端注册JavascriptInterface统一让js调用
class JsCallbackModel { lateinit var funData: String lateinit var funId: String lateinit var funName: String } abstract class JsFunctionCallBack(var funId: String) { abstract fun callback(respData: String?) abstract fun callback() abstract fun callback(respData: Boolean) } class JavaScriptCall(private val webView: WebView) { private fun jsCall(funName: String, funData: String, jsFunction: JsFunctionCallBack) { when(funName) { "screenCapture" -> { screenshot(funData.toInt(), jsFunction) } } } @JavascriptInterface fun jsCall(data: String) { // 将json字符串解析成kotlin对象 val gson = GsonBuilder().create() val model = gson.fromJson(data, JsCallbackModel::class.java) // 如果不存在函数名称 则忽略 if (model.funName == "") { return } val jsFunction: JsFunctionCallBack = object : JsFunctionCallBack(model.funId) { override fun callback(respData: String?) { if (webView == null) { return } if (funId.isEmpty()) { return } content.webView.post { webView.evaluateJavascript("jsBridgeApiCallBack('$funId', "$respData")", null) } } override fun callback() { if (funId.isEmpty()) { return } content.webView.post { webView.evaluateJavascript("jsBridgeApiCallBack('$funId')", null) } } override fun callback(respData: Boolean) { if (funId.isEmpty()) { return } content.webView.post { webView.evaluateJavascript("jsBridgeApiCallBack('$funId', '$respData')", null) } } } jsCall(model.funName, model.funData, jsFunction); } private fun screenshot(quality: Int, jsFunction: JsFunctionCallBack) { // 执行逻辑... // 执行js回调 jsFunction("base64...") } }
-
在js中传递回调函数调用Android截图
function screenshot(): Promise<string> { return new Promise(resolve => { androidCall("screenshot", (base64: string) => resolve(base64)) }) } screenshot().then(base64 => { })
-
-
Android传递回调给Javascript
原理跟Javascript传递回调给Android是一样的,具体实现如下:
-
Android端
class JavaScriptCall(private val webView: WebView) { companion object { val dataF: MutableMap<String, (callData: String) -> Unit> = mutableMapOf() fun jsCall(webView: WebView, funName: String, funData: String, funHandler: (callData: String) -> Unit) { val ctx: MutableMap<String, String> = mutableMapOf() ctx["funName"] = funName ctx["funData"] = funData val uuid = UUID.randomUUID().toString() ctx["funId"] = uuid dataF[uuid] = funHandler webView.post { val gson = GsonBuilder().create() val json = gson.toJson(ctx) webView.evaluateJavascript("jsCall('$json')", null) } } } @JavascriptInterface fun androidsBridgeApiCallBack(callUUID: String, callData: String) { val funHandler = dataF[callUUID]; if (funHandler != null) { funHandler(callData) dataF.remove(callUUID) } } }
-
Js端
function doSome(funId, data, callback) { // 执行逻辑... // 执行Android的回调函数 callback(funId, data) } function androidFunction(funId, respData: any?) { AndroidFunction.androidsBridgeApiCallBack(funId, respData) } function androidCallback(funId, funName, funData, fun) { switch (funName) { case 'doSome': { doSome(funId, funData, fun) } } } window.jsCall = function (json: string) { const obj = JSON.parse(json) const funName = obj['funName'] const funData = obj['funData'] const funId = obj['funId'] if (!funName) { return } androidCallback(funId, funName, funData, androidFunction) }
-
在Android中传递回调函数调用js的doSome
JavaScriptCall.jsCall(webView, "doSome", "data") { callParam -> Log.e("tAG", "回调参数: $callParam") }
-