原文链接:https://developer.chrome.com/native-client/devguide/coding/message-system
注意:已针对ChromeOS以外的平台公布了此处所述技术的弃用。
请访问我们的 迁移指南 了解详情。
消息系统
本节介绍用于在Native Client应用程序中的JavaScript代码与Native Client模块的C或C ++代码之间进行通信的消息传递系统。它介绍了异步编程的概念以及设置向JavaScript发送消息和从JavaScript接收消息的Native Client模块所需的基本步骤。本节假定您熟悉“ 应用程序结构”部分中提供的材料 。
这里使用“Hello,World”开始使用NaCl的示例来说明基本的编程技术。您可以/getting_started/part2
在Native Client SDK下载的目录中找到此代码。
参考信息
有关Pepper消息传递API的参考信息,请参阅以下文档:
- pp :: Instance类 HandleMessage(),PostMessage())
- pp ::模块类
- pp :: Var类
邮件系统简介
Native Client模块和JavaScript通过相互发送消息进行通信。消息的最基本形式是字符串。消息支持许多JavaScript类型,包括int,数组,数组缓冲区和字典(请参阅 pp :: Var, pp:VarArrayBuffer和常规消息传递系统文档)。由您决定消息类型并定义如何在JavaScript和Native Client端处理消息。对于“Hello,World”示例,我们仅使用字符串类型的消息。
当JavaScript将消息发布到Native Client模块时,HandleMessage()
会在模块端调用Pepper 函数。类似地,Native Client模块可以将消息发布到JavaScript,此消息会触发message
DOM中事件的JavaScript事件侦听器 。(有关更多信息,请参阅文档对象模型事件的W3C规范 。)在“Hello,World”示例中,用于发布和处理消息的JavaScript函数已命名postMessage()
并且 handleMessage()
(但可以使用任何名称)。在Native Client C ++端,用于发布和处理消息的Pepper Library函数是:
void pp::Instance::PostMessage(const Var &message)
virtual void pp::Instance::HandleMessage(const Var &message)
如果要从JavaScript接收消息,则需要pp::Instance::HandleMessage()
在Native Client模块中实现该 功能。
消息系统的设计
Native Client消息传递系统类似于浏览器允许Web工作者进行通信的系统(请参阅W3 Web工作者规范)。Native Client消息传递系统旨在使Native Client模块在后台执行可能繁重的处理时保持Web页面响应。当JavaScript向Native Client模块发送消息时,一旦将消息发送到Native Client模块,该postMessage()
调用就会返回。JavaScript不会等待Native Client的回复,从而避免陷入主JavaScript线程。在JavaScript方面,您设置了一个事件侦听器,以响应Native Client模块完成请求的处理并返回消息时发送的消息。
此异步处理模型使主线程保持空闲,同时避免以下问题:
- JavaScript引擎在等待同步调用返回时挂起。
- 当JavaScript入口点花费的时间超过一些时,浏览器会弹出一个对话框。
- 应用程序在等待无响应的Native Client模块时挂起。
“Hello,World”示例中的通信任务
以下部分描述了“Hello,World”示例如何在应用程序的JavaScript端和Native Client端发布和处理消息。
JavaScript代码
JavaScript代码和HTML中的“你好,世界”的例子可以在发现example.js
,common.js
和index.html
文件。重要的步骤是:
- 设置事件侦听器以侦听
message
Native Client模块中的事件。 - 实现事件处理程序调用以处理传入
message
事件的事件处理程序。 postMessage()
在页面加载后调用与NaCl模块通信。
第1步:来自common.js
function attachDefaultListeners() {
// The NaCl module embed is created within the listenerDiv
var listenerDiv = document.getElementById('listener');
// ...
// register the handleMessage function as the message event handler.
listenerDiv.addEventListener('message', handleMessage, true);
// ...
}
第2步:来自example.js
// This function is called by common.js when a message is received from the
// NaCl module.
function handleMessage(message) {
// In the example, we simply log the data that's received in the message.
var logEl = document.getElementById('log');
logEl.textContent += message.data;
}
// In the index.html we have set up the appropriate divs:
<body {attrs}>
<!-- ... -->
<div id="listener"></div>
<div id="log"></div>
</body>
第3步:来自example.js
// From example.js, Step 3:
function moduleDidLoad() {
// After the NaCl module has loaded, common.naclModule is a reference to the
// NaCl module's <embed> element.
//
// postMessage sends a message to it.
common.naclModule.postMessage('hello');
}
Native Client模块
“Hello,World”示例的Native Client模块中的C ++代码:
- 实现
pp::Instance::HandleMessage()
处理JavaScript发送的消息。 - 处理传入的消息。此示例仅检查JavaScript是否已发送“hello”消息而不是其他消息。
- 调用
PostMessage()
将确认发送回JavaScript代码。确认是Var
JavaScript代码可以处理的字符串形式。通常,app::Var
可以是多种JavaScript类型,请参阅消息传递系统文档。class HelloTutorialInstance : public pp::Instance { public: // ... // === Step 1: Implement the HandleMessage function. === virtual void HandleMessage(const pp::Var& var_message) { // === Step 2: Process the incoming message. === // Ignore the message if it is not a string. if (!var_message.is_string()) return; // Get the string message and compare it to "hello". std::string message = var_message.AsString(); if (message == kHelloString) { // === Step 3: Send the reply. === // If it matches, send our response back to JavaScript. pp::Var var_reply(kReplyString); PostMessage(var_reply); } } };
JavaScript代码中的消息传递:更多详细信息。
本节更详细地描述了“Hello,World”示例的JavaScript部分中的消息传递系统代码。
设置事件侦听器和处理程序
以下JavaScript代码为Native Client模块发布的消息设置事件侦听器。然后,它定义了一个消息处理程序,它只记录从模块接收的消息内容。
在加载时设置'message'处理程序
// From common.js
// Listen for the DOM content to be loaded. This event is fired when
// parsing of the page's document has finished.
document.addEventListener('DOMContentLoaded', function() {
var body = document.body;
// ...
var loadFunction = common.domContentLoaded;
// ... set up parameters ...
loadFunction(...);
}
// This function is exported as common.domContentLoaded.
function domContentLoaded(...) {
// ...
if (common.naclModule == null) {
// ...
attachDefaultListeners();
// initialize common.naclModule ...
} else {
// ...
}
}
function attachDefaultListeners() {
var listenerDiv = document.getElementById('listener');
// ...
listenerDiv.addEventListener('message', handleMessage, true);
// ...
}
实现处理程序
// From example.js
function handleMessage(message) {
var logEl = document.getElementById('log');
logEl.textContent += message.data;
}
请注意,该handleMessage()
函数将传递一个message_event,其中包含data
您可以在JavaScript中显示或操作的内容。“Hello,World”应用程序只是将此数据记录到log
div中。
Native Client模块中的消息传递:更多详细信息。
本节更详细地描述了“Hello,World”示例的Native Client模块部分中的消息传递系统代码。
实现HandleMessage()
如果希望Native Client模块接收和处理来自JavaScript的消息,则需要HandleMessage()
为模块的pp::Instance
类实现一个函数。该 HelloWorldInstance::HandleMessage()
函数检查从JavaScript发布的消息。首先,它检查它的类型 pp::Var
确实是一个字符串(不是双重等)。然后它将数据解释为带有的字符串var_message.AsString()
,并检查字符串是否匹配kHelloString
。在检查从JavaScript接收的消息之后,代码调用PostMessage()
将回复消息发送回JavaScript端。
namespace {
// The expected string sent by the JavaScript.
const char* const kHelloString = "hello";
// The string sent back to the JavaScript code upon receipt of a message
// containing "hello".
const char* const kReplyString = "hello from NaCl";
} // namespace
class HelloTutorialInstance : public pp::Instance {
public:
// ...
virtual void HandleMessage(const pp::Var& var_message) {
// Ignore the message if it is not a string.
if (!var_message.is_string())
return;
// Get the string message and compare it to "hello".
std::string message = var_message.AsString();
if (message == kHelloString) {
// If it matches, send our response back to JavaScript.
pp::Var var_reply(kReplyString);
PostMessage(var_reply);
}
}
};
实现特定于应用程序的功能
虽然“Hello,World”示例非常简单,但Native Client模块可能包含特定于应用程序的函数,以执行自定义任务以响应消息。例如,应用程序可以是压缩和解压缩服务(导出两个函数)。应用程序可以设置一个特定于应用程序的约定,来自JavaScript的消息是以冒号分隔的形式对<command>:<data>
。然后,Native Client模块消息处理程序可以沿着:
字符拆分传入的字符串,以确定要执行的命令。如果命令是“compress”,则要处理的数据是未压缩的字符串。如果命令是“uncompress”,则要处理的数据是已经压缩的字符串。在异步处理数据之后,应用程序然后将结果返回给JavaScript。
将消息发送回JavaScript代码
Native Client模块使用以下命令将消息发送回JavaScript代码PostMessage()
。Native Client模块始终pp::Var
以可由浏览器的JavaScript处理的形式返回其值。在此示例中,消息在Native Client模块的HandleMessage()
功能结束时发布:
PostMessage(var_reply);
发送和接收其他pp::Var
类型
除了字符串,pp::Var
还可以表示其他类型的JavaScript对象。例如,消息可以是JavaScript对象。这些更丰富的类型可以更容易地实现应用程序的消息传递协议。
要将NaCl模块中的字典发送到JavaScript,只需创建一个pp::VarDictionary
然后PostMessage
使用字典调用。
pp::VarDictionary dictionary;
dictionary.Set(pp::Var("command"), pp::Var(next_command));
dictionary.Set(pp::Var("param_int"), pp::Var(123));
pp::VarArray an_array;
an_array.Set(0, pp::Var("string0"));
an_array.Set(1, pp::Var("string1"))
dictionary.Set(pp::Var("param_array"), an_array);
PostMessage(dictionary);
以下是如何在JavaScript中创建类似对象并将其发送到NaCl模块:
var dictionary = {
command: next_command,
param_int: 123,
param_array: ['string0', 'string1']
}
nacl_module.postMessage(dictionary);
要在NaCl模块中接收字典类型的消息,请测试该消息是否真的是字典类型,然后使用pp::VarDictionary
该类转换消息。
virtual void HandleMessage(const pp::Var& var) {
if (var.is_dictionary()) {
pp::VarDictionary dictionary(var);
// Use the dictionary
pp::VarArray keys = dictionary.GetKeys();
// ...
} else {
// ...
}
}
CC-By 3.0许可下提供的内容