[Android]Android 插件化-续

前言

在上一篇文章中,叙述了Android 的三种完成插件的方法。不过在最终一种中,按照前面所叙述的内容完成有一个约束。

我们之前传递文件是经过evaluateJavascript,相当于注入javascript 代码,这种方法会约束我们传递的数据的长度,当我们传递一个大一点的文件就会呈现问题,亟待一个更好的方法完成文件的传递。

MessageChannel

在web 开发中,会运用iframe,将一个页面嵌入到另一个页面中,此刻,假如需要在这两个页面中传递数据,用的便是MessageChannel。不止页面之间能够运用这种方法,native 与WebView 中的页面也能够运用这种方法。

  1. 第一种

    在html 中,经过window 注册一个message 的listener

    window.addEventListener("message", onMessage);
    function onMessage(e) {
        ele.src = "data:image/png;base64," + e.data;
        // Use the transfered port to post a message back to the main frame
        e.ports[0].postMessage("Message back from the IFrame");
    }
    

    然后在Android 端,首要生成一对MessageChannel。回来的是一个数组,包括两个MessageChannel。

    然后是注册WebMessageCallback

    val baseUrl = "http://www.example.com"
    val messageChannel = if (WebViewFeature.isFeatureSupported(WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL)) {
        WebViewCompat.createWebMessageChannel(webView)
    } else null
    if (messageChannel != null && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK)) {
        messageChannel[0].setWebMessageCallback(object : WebMessagePortCompat.WebMessageCallbackCompat() {
            override fun onMessage(port: WebMessagePortCompat, message: WebMessageCompat?) {
                Log.d(TAG, "onMessage() called with: port = $port, message = ${message?.data}")
                super.onMessage(port, message)
            }
        })
    }
    

    然后在页面加载完成后发送音讯

    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)
        url ?: return
        view ?: return
        messageChannel?.let {
            if (WebViewFeature.isFeatureSupported(WebViewFeature.POST_WEB_MESSAGE)) {
                val webMessageCompat = WebMessageCompat(content, it)
                WebViewCompat.postWebMessage(view, webMessageCompat, Uri.parse(baseUrl))
            }
        }
    }
    

    注意加载内容的方法。假如我们的内容是本地文件,需要手动置顶一个origin

    webView.loadDataWithBaseURL(baseUrl, indexFile.readText(), null, null, null)
    

    假如是http 或许https 等自带地址的就无所谓了。

    理论上是这样啦,但是实际运用的时分会呈现过错

    java.lang.IllegalStateException: Port is already started

    一旦经过messageChannel[0].setWebMessageCallback 设置监听,然后再发送音讯就会呈现过错。

    假如不设置监听,发送音讯是没有问题的,但是这样双向的交流就变成了单向的了。不过,我们还有第二种

  2. 第二种

    在Android 端

    val baseUrl = "http://www.example.com"
    if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
        WebViewCompat.addWebMessageListener(webView, "test", setOf(baseUrl)) { view, message, sourceOrigin, isMainFrame, replyProxy ->
            Log.d(TAG, "onCreate() called with: view = $view, message = ${message.data}, sourceOrigin = $sourceOrigin, isMainFrame = $isMainFrame, replyProxy = $replyProxy")
        }
    }
    

    我们传递一个"test" ,然后我们在javascript 中便能够经过test 发送音讯以及监听数据。

    test.onmessage = function(event) {
        // prints "Got it!" when we receive the app's response.
        console.log(event.data);
    }
    test.postMessage("hello from yue-html")
    

    但是,这种方法也有一个问题,无法从native 自动发送音讯,只能在html 发送音讯之后,才能在addWebMessageListener 中经过replyProxy.postMessage 发送音讯。

最终

这种方法与evaluateJavascript 不同,MessageChannel 足以之后发送大文件。