Android与JavaScript通信(相互回调)
简述
在移动应用开发中,Android和JavaScript是两个常用的技术栈。Android作为主流的移动操作系统,而JavaScript则是用于网页前端和跨平台开发的脚本语言。为了实现更好的用户体验和功能扩展,Android与JavaScript之间的通信变得至关重要。本文将介绍Android与JavaScript之间的回调通信技巧
通信基础
通过 WebView 进行通信
Android 的 WebView 组件提供了
evaluateJavascript
方法,该方法可以执行 JavaScript 代码并获取返回结果。我们可以利用这一特性实现 Android 和 JavaScript 之间的通信。具体实现步骤如下:- 在 Android 代码中,通过
evaluateJavascript
方法执行 JavaScript 代码。
使用 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调用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")
}
链接:https://juejin.cn/post/7239617977364217915
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。