Google 针对Android应用开发者的安全指南包含以下内容:
WebView不
addJavaScriptInterface()
与不受信任的内容一起使用.在Android M及更高版本上,可以使用HTML消息通道.
近我可以告诉大家,"HTML消息通道"是指喜欢的东西createWebMessageChannel()
,WebMessagePort
,WebMessage
,和亲属.
但是,它们没有提供任何示例.他们所做的只是链接到WhatWG规范,这是相当不清楚的.而且,基于谷歌的搜索createWebMessageChannel
,它似乎还没有被广泛使用 - 我的博客文章描述了Android 6.0 SDK中的变化,使得排名前十的搜索结果,我只是顺便提一下.
addJavascriptInterface()
用于允许a中的JavaScript WebView
使用应用程序调用应用程序提供的Java代码WebView
.我们如何使用"HTML消息频道"代替它?
好吧,我有这个工作,虽然它有点糟糕.
第1步:填充你的WebView
使用loadDataWithBaseURL()
.loadUrl()
不行,因为错误.您需要为第一个参数使用http
或https
URL loadDataWithBaseURL()
(或者,至少不是file
,因为错误).您稍后将需要该URL,因此请保留它(例如,private static final String
值).
步骤2:确定何时要初始化从JavaScript到Java的通信.随着addJavascriptInterface()
,这是立即可用.但是,使用WebMessagePort
并不是那么好.特别是,在页面加载之前(例如,onPageFinished()
在a上WebViewClient
),您无法尝试初始化通信.
步骤3:在您想要初始化这些通信时,请createWebMessageChannel()
打开WebView
,创建一个WebMessagePort[]
.该数组中的第0个元素是通信管道的末尾,您可以调用setWebMessageCallback()
它来响应从JavaScript发送给您的消息.
第4步:手的第1个要素WebMessagePort[]
通过在包装它的JavaScript WebMessage
和调用postWebMessage()
上WebView
.postWebMessage()
将a Uri
作为第二个参数,并且Uri
必须从您在步骤1中使用的相同URL作为基本URL loadDataWithBaseURL()
.
@TargetApi(Build.VERSION_CODES.M) private void initPort() { final WebMessagePort[] channel=wv.createWebMessageChannel(); port=channel[0]; port.setWebMessageCallback(new WebMessagePort.WebMessageCallback() { @Override public void onMessage(WebMessagePort port, WebMessage message) { postLux(); } }); wv.postWebMessage(new WebMessage("", new WebMessagePort[]{channel[1]}), Uri.parse(THIS_IS_STUPID)); }
(其中,wv
是WebView
和THIS_IS_STUPID
与所使用的URL loadDataWithBaseURL()
)
步骤5:您的JavaScript可以为全局onmessage
事件分配一个函数,该函数将在调用时postWebMessage()
调用.ports
您在事件上获得的数组的第0个元素将是通信管道的JavaScript端,您可以将其填充到某个变量中.如果需要,您可以为该onmessage
端口分配一个函数,如果Java代码将使用它WebMessagePort
来发送未来数据.
步骤#6:当您想要从JavaScript向Java发送消息时,请postMessage()
从步骤#5中调用端口,该消息将被传递到您setWebMessageCallback()
在步骤3中注册的回调.
var port; function pull() { port.postMessage("ping"); } onmessage = function (e) { port = e.ports[0]; port.onmessage = function (f) { parse(e.data); } }
此示例应用程序演示了该技术.它有一个WebView
显示基于环境光传感器的当前光级.传感器数据WebView
在推送的基础上(当传感器改变时)或在拉动的基础上(用户点击网页上的"光级"标签)输入.此应用程序WebMessagePort
在Android 6.0+设备上使用这些应用程序,虽然推送选项已注释掉,因此您可以确认拉动方法正在通过端口工作.我将在即将出版的本书中详细介绍示例应用程序.
在CTS中有一个测试
// Create a message channel and make sure it can be used for data transfer to/from js. public void testMessageChannel() throws Throwable { if (!NullWebViewUtils.isWebViewAvailable()) { return; } loadPage(CHANNEL_MESSAGE); final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel(); WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]}); mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI)); final int messageCount = 3; final CountDownLatch latch = new CountDownLatch(messageCount); runTestOnUiThread(new Runnable() { @Override public void run() { for (int i = 0; i < messageCount; i++) { channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE + i)); } channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() { @Override public void onMessage(WebMessagePort port, WebMessage message) { int i = messageCount - (int)latch.getCount(); assertEquals(WEBVIEW_MESSAGE + i + i, message.getData()); latch.countDown(); } }); } }); // Wait for all the responses to arrive. boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS); }
档案:cts/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
.至少有一些起点.