前言
在上一篇文章中,讲述了Android 的三种实现插件的方式。不过在最后一种中,按照前面所讲述的内容实现有一个限制。
我们之前传递文件是通过evaluateJavascript,相当于注入javascript 代码,这种方式会限制我们传递的数据的长度,当我们传递一个大一点的文件就会出现问题,亟待一个更好的方式实现文件的传递。
MessageChannel
在web 开发中,会使用iframe,将一个页面嵌入到另一个页面中,此时,如果需要在这两个页面中传递数据,用的就是MessageChannel。不止页面之间可以使用这种方式,native 与WebView 中的页面也可以使用这种方式。
-
第一种
在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。
然后是注册
WebMessageCallbackval 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设置监听,然后再发送消息就会出现错误。如果不设置监听,发送消息是没有问题的,但是这样双向的沟通就变成了单向的了。不过,我们还有第二种
-
第二种
在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 足以之后发送大文件。