《成为大前端》系列 4.4 Native与JS通信-参数传递和结果返回(Android)

1,024 阅读2分钟

JS 传递参数到 Native

前面完成了 JS 调用 Native,接下来继续 JS 如何传递参数到 Native

传递原始类型数据

先看 JS 端的代码:

function onClickButton() {
  window.androidBridge.callNative("Hello");
}

Native 端:

inner class BridgeObject {
    @JavascriptInterface
    // 声明一个String类型参数对应JS
    fun callNative(arg: String) {
        Log.e("WebView", "callNative ok. args is $arg")
    }
}

运行,点击按钮 Logcat 里输出:

E/WebView: callNative ok. args is Hello

传递 json 对象

Android 没有办法传递 json 对象,因此我们利用 JSON.stringify 转换为 string:

window.androidBridge.callNative(
  JSON.stringify({
    name: "mingo"
  })
);

Native:

fun callNative(arg: String) {
    Log.e("WebView", "callNative ok. args is $arg")
    val json = JSONObject(arg)
    val name = json.getString("name")
    Log.e("WebView", "name is $name")
}

Logcat 输出:

E/WebView: callNative ok. args is {"name":"mingo"}
E/WebView: name is mingo

JS 传递参数到 Native 只能传递原始数据类型,JSON 里也只能包含原始数据类型 Function/Class 之类的是不能传递到 Native 的,因此他们之间实际是信息交 换,而不是同一语言的调用关系。

Native 返回结果给 JS

方法一: 直接通过 JavascriptInterface 的方法返回

Native 改动:

// 声明返回值为String类型
fun callNative(arg: String): String {
    Log.e("WebView", "callNative ok. args is $arg")
    // 返回
    return "This is ative result."
}

页面的改动:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0"
    />
    <style type="text/css">
      html,
      body {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
      }
      button {
        display: block;
        width: 100%;
        height: 30%;
        font-size: 50px;
      }
      // 调整输出的字体大小
      pre {
        font-size: 40px;
      }
    </style>
  </head>
  <body>
    <script type="text/javascript">
      function onClickButton() {
        // 获取返回值
        var result = window.androidBridge.callNative(
          JSON.stringify({
            name: "mingo"
          })
        );
        var logEl = document.getElementById("log");
        logEl.innerText += result + "\n";
      }
    </script>

    <!-- 打算点击按钮调用native -->
    <button onclick="onClickButton()">Call Native</button>

    <pre id="log">
<!-- 用于显示js的日子 -->
</pre>
  </body>
</html>

运行后,点击按钮,结果如图:

方法二: 通过 evaluateJavascript

方法一是通过返回值传递给 JS 的,我们还有另一种方式,通过webView.evaluateJavascript的来实现。

webView.evaluateJavascript("console.log('This is ative result.')", null)

来看看怎么实现

首先是页面端声明一个函数用来给 webView 运行:

<script type="text/javascript">
  function onClickButton() {
    window.androidBridge.callNative(
      JSON.stringify({
        name: "mingo"
      })
    );
  }

  // 声明onNativeResult给webView运行
  function onNativeResult(result) {
    var logEl = document.getElementById("log");
    logEl.innerText += result + "\n";
  }
</script>

Native 端改动:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        WebView.setWebContentsDebuggingEnabled(true);

        webView.settings.javaScriptEnabled = true
        webView.webViewClient = WebViewClient()

        // 在加载网页前添加我们的js对象
        // 并且传递了webView进去
        webView.addJavascriptInterface(BridgeObject(webView), "androidBridge")

        // 加载assets中的网页
        webView.loadUrl("file:///android_asset/test.html")
    }

    // 构造还是接收webView作为成员变量
    inner class BridgeObject(val webView: WebView) {

        @JavascriptInterface
        fun callNative(arg: String) {
            Log.e("WebView", "callNative ok. args is $arg")

            // 这里使用post是因为evaluateJavascript只能运行在WebView规定的线程
            webView.post {
                // 执行js代码,运行onNativeResult方法
                webView.evaluateJavascript("window.onNativeResult('This is native result.')", null)
            }
        }
    }
}

运行后,点击按钮,结果如图:

恭喜,到这里已经完成了 Native 返回结果给 JS 端了。