Chrome插件开发入门(二)——消息传递机制

Blog | Qiushi Chen 2014-03-31 9538 阅读

由于插件的js运行环境有区别,所以消息传递机制是一个重要内容。阅读了很多博文,大家已经说得很清楚了,直接转一篇@姬小光 的博文,总结的挺好。后面附一个自己写过的demo,基本就对消息传递能够熟悉了。

在开发 Chrome 扩展时经常需要在页面之间进行通讯,比如 background 与 content script 之间,background 与 popup 之间等等,本文结合官方文档中的例子介绍了 chrome 扩展开发中消息传递的基本实现。

一般情况下,我们会让 background 来控制一切,将扩展的主要逻辑都放在 background 中比较便于管理。其它页面可以通过消息传递的机制与 background 进行通讯。

理论上 content script 与 popup 之间也可以传递消息,但不建议这么做。

对于 background 和 popup 之间,其实可以直接相互调用对方的方法,也不需要消息传递。那么消息传递其实主要就是 content script 和 background 之间进行的了。

在 Chrome 扩展内部的消息传递分为两种,一种是单次的消息请求,另外一种是长连接。下面详细举例说明。

简单的单次请求(Simple one-time requests)

如果只是想简单的给扩展的其它页面发送一次消息,那么可以使用 runtime.sendMessage 或者 tabs.sendMessage 方法,它允许你发送单次的JSON序列化后的数据,可以从 content script 发送给扩展页面,反之亦然。也可以选择性地传入一个处理响应的回调函数。

从 content script 发送请求代码如下:

contentscript.js
================
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});

从扩展页面发送给 content script 也很类似,只是需要指定发送给那个 tab,下面的例子即为向选中的 tab 发送消息:

background.html
===============
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
});
});

注:官方例子中的 getSelected 方法已经被废弃了,可以使用 chrome.tabs.query({active:true}, function(tab) {}) 来替代。

在消息接收完毕时,需要设置一个  runtime.onMessage 事件监听器来处理消息,这部分在 content script 和扩展页面种都是一样的:

chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
});

注意:如果有多个页面同时监听 onMessage 事件,那么只有第一个调用 sendResponse() 的页面可以成功返回响应信息,其它的都会被忽略。

小技巧:当事件监听器返回时函数就不可用了,不过如果让函数 return: true 的话,可以让该函数异步响应,直到调用 sendResponse 后才结束,具体说明请见文档

长连接(Long-lived connections)

有时候我们需要在 content script 和扩展页面之间保持一个长期的通讯信道,那么你可以分别使用 runtime.connect 和 tabs.connect 来建立长连接信道。可以选择性地给信道命名,方便你区分不同类型的连接。

每个连接建立完成后都会返回一个 runtime.Port 对象,用来在连接之间发送和接收消息。

下面的例子展示了如何从 content script 建立连接,并发送和接收消息:

contentscript.js
================
var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
if (msg.question == "Who's there?")
port.postMessage({answer: "Madame"});
else if (msg.question == "Madame who?")
port.postMessage({answer: "Madame... Bovary"});
});

从扩展页面建立连接也很类似,只是要指定要连接到哪个tab。最简单地可以把上面的连接的代码改成 tabs.connect 即可。

为了能够处理连接请求,需要设置一个  runtime.onConnect 事件监听器,在 content script 和扩展页面中中都是一样的。当扩展的某一部分调用了 “connect() ”,该事件即被触发,然后就可以使用 runtime.Port 对象来发送和接收消息了。响应连接的代码大致如下所示:

chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "knockknock");
port.onMessage.addListener(function(msg) {
if (msg.joke == "Knock knock")
port.postMessage({question: "Who's there?"});
else if (msg.answer == "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer == "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});

或许有时你还想知道连接何时关闭,那么你可以监听 runtime.Port.onDisconnect 事件,当通讯的任意一端调用  runtime.Port.disconnect,或者包含该端口的页面已被卸载。onDisconnect 事件可以保证在每个端口上只触发一次。

另外,消息传递还包括不同扩展之间的消息传递,还有 Chrome 与本地程序之间的消息传递,这里就不介绍了,感兴趣的同学可以直接查看官方文档

—————————————–分割线——————————————-

demo:

几个最基本的文件

在这里,先假设大家对chrome插件开发的最基本知识已有所掌握。例如什么是manifest.json,什么是background.html等。

manifest.json

{
"name": "A browser action with a popup that changes the page color.",
"version": "1.0",
"permissions":["tabs","<all_urls>"],
"browser_action": {
"default_icon": "icon.png"
},
"background": {
"page": "background.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js" : ["jquery-1.7.1.js","injectscript.js"]
}
],
"manifest_version": 2
}

background.html

<!DOCTYPE html>
<html>
<head>
<title>bg</title>
<script type="text/javascript" src="http://blog.chenqiushi.com/2014/03/31/chrome%e6%8f%92%e4%bb%b6%e5%bc%80%e5%8f%91%e5%85%a5%e9%97%a8%ef%bc%88%e4%ba%8c%ef%bc%89-%e6%b6%88%e6%81%af%e4%bc%a0%e9%80%92%e6%9c%ba%e5%88%b6/bg.js"><script>
</head>
<body>
hello
<body>
</html>

这里引用了一个后台处理程序,bg.js,后面会讲到。

扩展程序发送请求数据到内容脚本,内容脚本给出回应

扩展程序后台脚本bg.js

(function(){
chrome.browserAction.onClicked.addListener(function(tab) {
// 扩展向内容脚本发送消息
chrome.tabs.sendMessage(tab.id,{
greeting: "hello to content script!"
}, function(response) {
console.log(response.farewell);
});
});
})();

内容脚本injectscript.js

(function(){
console.log("injected"); var resOK = {
farewell: "content script send response back..."
}; var resError = {
farewell: "content script hasError!"
}; chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
console.log("Request comes from extention " + sender.tab.url); if (request.greeting === "hello to content script!"){
sendResponse(resOK);
}else{
sendResponse(resError);
}
});
})();

扩展程序向内容脚本发送一条消息hello to content script!,内容脚本接收到这条消息后去判断是不是那句话,如果是,就返回resOK对象,如果不是,就返回resError对象。

这时,扩展程序收到内容脚本的一条回应,至此,此番通话就结束了。

看一下结果截图

内容脚本发送请求数据到扩展程序,扩展程序给出回应

扩展程序后台脚本bg.js

(function(){

        var resOK = {
farewell: "extension send response back..."
}; var resError = {
farewell: "extension hasError!"
}; chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
console.log("Request comes from content script " + sender.tab.url); if (request.greeting === "hello to extention!"){
sendResponse(resOK);
}else{
sendResponse(resError);
}
}); })();

内容脚本injectscript.js

(function(){
console.log("injected"); chrome.extension.sendMessage({greeting: "hello to extention!"}, function(response) {
console.log(response.farewell);
});
})();

内容脚本向扩展程序发送一条消息hello to extention!,扩展程序接收到这条消息后去判断是不是那句话,如果是,就返回resOK对象,如果不是,就返回resError对象。

这时,内容脚本收到扩展程序的一条回应,至此,此番通话就结束了。

特别应该注意的是:扩展程序向内容脚本发送请求数据时用的是chrome.tabs.sendMessage,反过来,用的是chrome.extension.sendMessage

看一下结果截图

如果以后还有一些chrome插件的学习总结,还会写在这里。

demo地址:
点击下载demo

The post Chrome插件开发入门(二)——消息传递机制 appeared first on Blog | Qiushi Chen.

Chrome 消息传递机制的更多相关文章

  1. Chrome插件开发入门(二)——消息传递机制

    Chrome插件开发入门(二)——消息传递机制   由于插件的js运行环境有区别,所以消息传递机制是一个重要内容.阅读了很多博文,大家已经说得很清楚了,直接转一篇@姬小光 的博文,总结的挺好.后面附一 ...

  2. Storm内部的消息传递机制

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 一个Storm拓扑,就是一个复杂的多阶段的流式计算.Storm中的组件 ...

  3. 我理解的Hanlder--android消息传递机制

    每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与Ac ...

  4. Handler消息传递机制

    引言: 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题. 为了解决这个问题,Android制定了一条简单的规则:只允许UI线程 ...

  5. Android学习之Handler消息传递机制

    Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户 ...

  6. ios 消息传递机制

    引用文章 一.KVO 1.当对象中的某个属性值发生了改变,可以对这些值的观察者做出通知. 2.接受者(会接收到值发生改变的消息) 必须知道发送者(值将发生改变的那个对象). 3.接收者同样还需要知道发 ...

  7. iOS开发——OC篇&消息传递机制(KVO/NOtification/Block/代理/Target-Action)

     iOS开发中消息传递机制(KVO/NOtification/Block/代理/Target-Action)   今晚看到了一篇好的文章,所以就搬过来了,方便自己以后学习 虽然这一期的主题是关于Fou ...

  8. Android异步消息传递机制源码分析

    1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...

  9. (Android数据传递)Intent消息传递机制 “Intent”“数据传递”

    Intent类的继承关系:   需要注意的是,该类实现了Parcelable(用于数据传递)和Cloneable接口. Intent是一种(系统级别的)消息传递机制,可以在应用程序内使用,也可以在应用 ...

随机推荐

  1. UVa 111 - History Grading (by 最长公共子序列 )

     History Grading  Background Many problems in Computer Science involve maximizing some measure accor ...

  2. git提交报错

    Error occurred computing Git commit diffsMissing unknown 0000000000000000000000000000000000000000 co ...

  3. 基于WF4.0的公文管理系统

    系统功能说明 公文管理 通过定义公文的基本信息,并将它按照工作流的定义流转实现公文的管理.包含以下功能: )公文创建:用户能够将格式化文本作为公文上传到系统中,并选择工作流启动流程. )公文审批:具有 ...

  4. C标准头文件概述

    C的C89标准一共定义了15个头文件,这些头文件具有幂等性(多次包含同一个头文件的效果等同于只包含了一个头文件,例外),独立性(每个标准头文件的正常工作都不需要以包含其他标准头文件为前提,也没有任何标 ...

  5. Python pass语句作用与用法

    Python中的pass语句作用是什么?表示它不做任何事情,一般用做占位语句.pass语句具体作用及使用方法,我们往下看. pass语句在函数中的作用 当你在编写一个程序时,执行语句部分思路还没有完成 ...

  6. docker基础命令详解

    Commands: attach    Attach to a running container build     Build an image from a Dockerfile commit  ...

  7. Visualize real-time data streams with Gnuplot

    源文地址 (September 2008) For the last couple of years, I've been working on European Space Agency (ESA) ...

  8. testng环境设置

    1.在eclipse中安装testng插件,地址:http://beust.com/eclipse 2.设置testng环境变量 testng_home D:\Program Files\eclips ...

  9. Linux基于libmemcached,php扩展memcached的安装

    安装环境:CentOS 6.4 php的扩展memcache,不支持cas,所以我们要装memcached扩展,memcached扩展是基于libmemcached,所以要先安装libmemcache ...

  10. css继承性和不继承的属性。

    在CSS中并不是所有的属性都是能够继承的,因此在使用时一定要了解哪些是能够继承的哪些是不能够继承的.visibility和cursor能够被所有元素继承:letter-spacing,word-spa ...