ipdl就是用一种安全的方式实现进程或线程间通信,kaios里面最直白的使用IPDL的原因就是:有些XPCOM,或者有些功能,函数,必须在主进程跑。如果这个时候你在子进程,你就需要告诉你的父进程去做那些功能。
ipdl的知识需要从官网去学:
https://developer.mozilla.org/en-US/docs/Mozilla/IPDL
学习完了发现要自己创建一个新的还是挺困难的,这里我创建了一个最简单的模板。
首先要知道已经存在的ipdl叫PContent,可以利用它来管理我们自己创建的ipdl
1.创建ipdl文件
创建ipdl文件geck/dom/aaatest/ipc/PAaaTest.ipdl
include protocol PContent;
namespace mozilla {
namespace dom {
namespace aaatest {
async protocol PAaaTest {
//利用PContent来管理PAaaTest的生命周期,创建和销毁PAaaTest的实例
manager PContent;
parent:
//虚构函数
__delete__();
//parent 接收的消息
SetTestData();
};
}// namespace aaatest
}// namespace dom
}// namespace mozilla
解析:
这里include的PContent作为管理者,来管理PAaaTest的整个生命周期。PAaaTest.ipdl编译之后会自动生成PAaaTestParent.h和PAaaTestChild.h,而PAaaTestParent和PAaaTestChild是抽象类,使用者必须写这两个类的继承函数。
在这个ipdl里面,我们只定义了一个函数消息SetTestData(),这个消息写在parent下面,表示这个消息是child发给parent的消息,即PAaaTestParent里面需要有RecvSetTestData,PAaaTestChild里面需要有SendSetTestData。
需要注意的是,所有outgoing message可以直接调用,所有incoming message都是纯虚函数,必须被子类实现。所以,我们写子类的时候只需要在parent里面写RecvSetTestData就可以了。
2.写父子类,包括:
-AaaTestParent.h
-AaaTestParent.cpp
-AaaTestChild.h
-AaaTestChild.cpp
(1)Gecko/dom/aaatest/AaaTestParent.h
#ifndef mozilla_dom_aaatest_AaaTestParent_h
#define mozilla_dom_aaatest_AaaTestParent_h
#include "mozilla/dom/aaatest/PAaaTestParent.h"
namespace mozilla {
namespace dom {
namespace aaatest {
class AaaTestParent :public PAaaTestParent
,public nsISupports
{
public:
NS_DECL_ISUPPORTS
AaaTestParent();
virtual~AaaTestParent();
virtualvoid ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
protected:
/*receive child function call*/
virtualbool RecvSetTestData() MOZ_OVERRIDE;
};
}// namespace aaatest
}// namespace dom
}// namespace mozilla
#endif /*mozilla_dom_aaatest_AaaTestParent_h*/
(2)Gecko/dom/aaatest/AaaTestParent.cpp
#include "AaaTestParent.h"
namespace mozilla {
namespace dom {
namespace aaatest {
NS_INTERFACE_MAP_BEGIN(AaaTestParent)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(AaaTestParent)
NS_IMPL_RELEASE(AaaTestParent)
AaaTestParent::AaaTestParent(){
//LOG("enter\n");
MOZ_COUNT_CTOR(AaaTestParent);
}
AaaTestParent::~AaaTestParent(){
//LOG("enter\n");
MOZ_COUNT_DTOR(AaaTestParent);
}
void
AaaTestParent::ActorDestroy(ActorDestroyReason aWhy){
}
bool
AaaTestParent::RecvSetTestData(){
//LOG("enter\n");
return true;
}
}// namespace jrdfota
}// namespace dom
}// namespace mozilla mozilla
ActorDestroy也是Parent类中必须去实现的虚函数,这个在PAaaTestParent中规定的。在ActorDestroy中可以去写销毁资源的语句。
(3)Gecko/dom/aaatest/AaaTestChild.h
#ifndef mozilla_dom_aaatest_AaaTestChild_h
#define mozilla_dom_aaatest_AaaTestChild_h
#include "mozilla/dom/aaatest/PAaaTestChild.h"
namespace mozilla {
namespace dom {
namespace aaatest {
class AaaTestChild :public PAaaTestChild
{
public:
AaaTestChild();
virtual~AaaTestChild();
};
}// namespace aaatest
}// namespace dom
}// namespace mozilla
#endif
(4)Gecko/dom/aaatest/AaaTestChild.cpp
#include "AaaTestChild.h"
usingnamespace mozilla;
usingnamespace mozilla::dom;
usingnamespace mozilla::dom::aaatest;
AaaTestChild::AaaTestChild(){
MOZ_COUNT_CTOR(AaaTestChild);
}
AaaTestChild::~AaaTestChild(){
MOZ_COUNT_DTOR(AaaTestChild);
}
可以看到AaaTestChild中其实什么也没做,但是我们必须写一个继承PAaaTestChild的类。
(5)补充
在gecko/dom/aaatest/moz.build中,我们需要添加关于ipdl的信息,不然会编译报错
EXPORTS.mozilla.dom.aaatest +=[
'AaaTestChild.h',
'AaaTestParent.h',
'MozAaaTest.h',
]
SOURCES +=[
'AaaTestChild.cpp',
'AaaTestParent.cpp',
'MozAaaTest.cpp',
]
LOCAL_INCLUDES +=[
'/dom/aaatest',
]
IPDL_SOURCES +=[
'ipc/PAaaTest.ipdl',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY ='xul'
3.把PContent的路搭通,还需要修改:
-PContent.ipdl
-ContentParent.h
-ContentParent.cpp
-ContentChild.h
-ContentChild.cpp
来看看具体修改
(1)gecko/dom/ipc/PContent.ipdl
include protocol PAaaTest;
prio(normal upto urgent) intr protocol PContent
{
manages PAaaTest;
parent:
PAaaTest();//构造函数
}
解析:
PContent作为MOZILLA最高层的protocol之一,会作为一个工厂来管理依附于他的sub-protocol。‘manages’的使用申明了PContent对PAaaTest的管理,使得PContent必须为PAaaTest申明构造和虚构函数;另外,‘manages’也意味着PAaaTest被绑定到PContent的生命周期里,如果PContent实例销毁,所以依附于他的sub-protocol都会被销毁,即PAaaTest也会被销毁。
构造函数与析构函数的写的位置不一样,这个是协议规定的,不可更改。构造函数在PContent.ipdl中(如PAaaTest()),析构函数在PAaaTest.ipdl中(如 __delete__())。__delete__()是唯一可以不用写执行函数的IPDL消息,然而,在某些情况也可以写。
(2)gecko/dom/ipc/ContentParent.h,下面的修改基本都是固定格式,不要解释
namespace mozilla {
namespace dom {
class ContentParent MOZ_FINAL :public PContentParent
{
public:
virtual PAaaTestParent* AllocPAaaTestParent();
virtual bool DeallocPAaaTestParent(PAaaTestParent*);
}
}
}
(3)gecko/dom/ipc/ContentParent.cpp
#include "mozilla/dom/aaatest/AaaTestParent.h"//PAaaTestParent的执行
usingnamespace mozilla::dom::aaatest;
//构造函数,函数名格式固定Alloc~
PAaaTestParent*
ContentParent::AllocPAaaTestParent()
{
AaaTestParent* parent =new AaaTestParent();
parent->AddRef();
return parent;
}
// 析构函数,函数名固定Dealloc~
bool
ContentParent::DeallocPAaaTestParent(PAaaTestParent* aAaaTest)
{
static_cast(aAaaTest)->Release();
returntrue;
}
(4)gecko/dom/ipc/ContentChild.h
namespace mozilla {
namespace dom {
class ContentChild :public PContentChild
{
public:
virtual PAaaTestChild* AllocPAaaTestChild();
virtual bool DeallocPAaaTestChild(PAaaTestChild*);
}
}
}
(5) gecko/dom/ipc/ContentChild.cpp
#include "mozilla/dom/aaatest/AaaTestChild.h"//PAaaTestChild的执行
usingnamespace mozilla::dom::aaatest;
//构造函数,函数名格式固定Alloc~
PAaaTestChild*
ContentChild::AllocPAaaTestChild()
{
returnnew AaaTestChild();
}
// 析构函数,函数名固定Dealloc~
bool
ContentChild::DeallocPAaaTestChild(PAaaTestChild* aAaaTest)
{
delete aAaaTest;
returntrue;
}
4.ipdl的使用,这个例子是在我前面写的文章《如何添加一个webidl》的例子的延续
首先需要判断是否主进程
在gecko/dom/MozAaaTest.cpp中写SetTestData函数,如果是非b2g进来就可以利用child给parent发消息,直接取得child的实例,调用child的send函数。
(1)头文件gecko/dom/aaatest/MozAaaTest.h
#ifndef mozilla_dom_aaatest_MozAaaTest_h
#define mozilla_dom_aaatest_MozAaaTest_h
#include "nsWrapperCache.h"
#include "nsPIDOMWindow.h"
#include "Types.h"
#include "AaaTestChild.h"
namespace mozilla {
namespace dom {
namespace aaatest {
class MozAaaTest MOZ_FINAL
:public nsISupports
,public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MozAaaTest)
MozAaaTest(nsPIDOMWindow* aWindow);
virtual ~MozAaaTest();
nsPIDOMWindow* GetParentObject()const{return mWindow;}
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
/* Impliment the WebIDL interface begin*/
NS_IMETHODIMP SetTestData();
/* Imppliment the WebIDL interface end*/
private:
PAaaTestChild* _GetAaaTestChild();
PAaaTestChild* mAaaTestChild;
protected:
nsCOMPtr mWindow;
};
}// namespace aaatest
}// namespace dom
}// namespace mozilla
#endif
(2)Cpp文件gecko/dom/aaatest/MozAaaTest.cpp
#include "MozAaaTest.h"
#include "mozilla/dom/MozAaaTestBinding.h"
#include "nsXULAppAPI.h"//判断是否主进程
#include "mozilla/dom/ContentChild.h"//利用PContent构造child类
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::dom::aaatest;
////////////////////////////////////////////////////////////////
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MozAaaTest)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(MozAaaTest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MozAaaTest)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MozAaaTest, mWindow)
//////////////////////////////////////////////////////////////////
MozAaaTest::MozAaaTest(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
{
LOG("enter MozAaaTest enter\n");
mAaaTestChild = NULL;
}
MozAaaTest::~MozAaaTest()
{
LOG("enter ~MozAaaTest enter\n");
mAaaTestChild = NULL;
}
//////////////////////////////////////////////////////////////////
JSObject*
MozAaaTest::WrapObject(JSContext* aCx)
{
return MozAaaTestBinding::Wrap(aCx,this);
}
NS_IMETHODIMP MozAaaTest::SetTestData(){
LOG("MozAaaTest::SetTestData\n");
// 判断是否主进程
if(GeckoProcessType_Default == XRE_GetProcessType()){
//从b2g进程进来会进入main
LOG("main process\n");
}else{
// 从非b2g进程进来会进入child
LOG("child process\n");
//child直接调用send函数发送消息,parent会收到消息,调用Recv函数。
_GetAaaTestChild()->SendSetTestData();
}
return NS_OK;
}
// 获取child的实例,利用PContent中的构造函数
PAaaTestChild*
MozAaaTest::_GetAaaTestChild(){
if(!mAaaTestChild){
mAaaTestChild = ContentChild::GetSingleton()->SendPAaaTestConstructor();
}
return mAaaTestChild;
}