注册

Android与JavaScript通信(相互回调)

简述


     在移动应用开发中,Android和JavaScript是两个常用的技术栈。Android作为主流的移动操作系统,而JavaScript则是用于网页前端和跨平台开发的脚本语言。为了实现更好的用户体验和功能扩展,Android与JavaScript之间的通信变得至关重要。本文将介绍Android与JavaScript之间的回调通信技巧


通信基础




  1. 通过 WebView 进行通信


    Android 的 WebView 组件提供了 evaluateJavascript 方法,该方法可以执行 JavaScript 代码并获取返回结果。我们可以利用这一特性实现 Android 和 JavaScript 之间的通信。具体实现步骤如下:



    1. 在 Android 代码中,通过 evaluateJavascript 方法执行 JavaScript 代码。



  2. 使用 JavaScriptInterface 实现通信


    我们可以使用 JavascriptInterface 接口实现JavaScript 与 Android 的通信。具体实现步骤如下:



    1. 在 Android 代码中创建一个类,实现 JavascriptInterface 接口。
    2. 在该类中定义需要供 JavaScript 调用的方法,并添加 @JavascriptInterface 注解。
    3. 在 JavaScript 中通过 window.AndroidFunction 对象调用 Android 代码中的方法,实现通信,其中AndroidFunction是注册JavascriptInterface时指定的, 如下所示:
      webView.addJavascriptInterface(new Object() {
      @JavascriptInterface
      public void jsCallback(String message) {
      // ...
      }
      }, "AndroidFunction");





Android调用JavaScript函数




  1. 忽略返回值

        val webView = findViewById<WebView>(R.id.webView)

    webView.evaluateJavascript("jsFunction('message')", null)




  2. 获取返回值

        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()


双向回调通信




  1. Javascript传递回调给Android


    上述的 AndroidFunction.jsCallback(message)方式目前只能传递字符串,如果不做特殊处理,是无法执行回调函数的, 执行 AndroidFunction.jsCallback(message)时,在Android中获取到的是字符串,这时将回调函数转换成回调令牌,然后通过令牌执行相应的回调函数,步骤如下:




    1. 在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))

      }



    2. 在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...")
      }
      }




    3. 在js中传递回调函数调用Android截图

      function screenshot(): Promise<string> {
      return new Promise(resolve => {
      androidCall("screenshot", (base64: string) => resolve(base64))
      })
      }
      screenshot().then(base64 => {
      })





  2. Android传递回调给Javascript


    原理跟Javascript传递回调给Android是一样的,具体实现如下:




    1. 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)
      }
      }
      }



    2. 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)
      }



    3. 在Android中传递回调函数调用js的doSome

      JavaScriptCall.jsCall(webView, "doSome", "data") { callParam ->
      Log.e("tAG", "回调参数: $callParam")
      }

作者:晟东
链接:https://juejin.cn/post/7239617977364217915
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册