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 |
(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); } |
(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<AaaTestParent*>(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<nsPIDOMWindow> mWindow;
};
}// namespace aaatest }// namespace dom }// namespace mozilla
#endif
|
#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; }
|