目录
鸿蒙APP
APP框架代码组成
目录 | 描述 |
foundation/aafwk | APP运行框架代码 |
foundation/aafwk/frameworks | 普通APP进程框架代码,运行于每个普通APP进程实例中 |
foundation/aafwk/services | APP管理进程代码,管理所有普通的APP进程,是所有APP进程信息汇总的地方 |
foundation/aafwk/interfaces | APP运行框架代码头文件集散地 |
APP运行框架代码
app运行框架代码作用于每个普通APP进程中。普通APP进程和APP管理进程之间联系紧密,因此运行框架代码中有一部分主要负责与管理进程之间的交互逻辑,另一部分是常规其他逻辑。
目录 | 描述 |
foundation/aafwk/frameworks/abilitymgr_lite | 与管理进程交互逻辑部分 |
foundation/aafwk/frameworks/ability_lite | 其它正常逻辑 |
foundation/aafwk/frameworks/want_lite | want结构为APP和APP管理进程都会用到的数据结构,主要用于传递进程间的数据,本目录主要实现want的序列化和反序列化以及set和get |
APP管理进程代码
本部分代码主要实现APP管理进程的逻辑,此进程存在的目的就是为了支持其它普通APP进程的正常运行。另外,本部分代码还包含了一个基本的测试代码,此测试代码使用shell(命令行)启动,为一个独立的进程,用来测试APP管理进程和普通APP进程的基本功能(可以认为是一种冒烟测试)。
目录 | 描述 |
foundation/aafwk/services/abilitymgr_lite/src | APP管理进程实现代码 |
foundation/aafwk/services/abilitymgr_lite/tools | 测试工具代码 |
APP基本概念
token
token为一个运行中的APP的整数标识,不同的APP具有不同的token,在系统中,特别是在APP管理进程中,通过不同的token来区分不同的APP,可以说token是系统中APP的身份标识。在创建APP进程时,会生成token字段,代码如下。
namespace OHOS {
constexpr uint8_t TIME_LENGTH = 32;
int32_t TokenGenerate::token_ = 0;
uint64_t TokenGenerate::GenerateToken()
{
int curSecond = time(nullptr);
if (token_ == INT32_MAX - 1) {
token_ = 0;
}
return (((uint64_t) curSecond << TIME_LENGTH) + ((uint64_t) token_++));
}
} // namespace OHOS
从上述代码看出,token的生成与时间有关,这样保证了不同的APP具有不同的token值。
APP类型
APP分为2大类型。PAGE类型和SERVICE类型。即有界面类型和服务类型。有界面类型类似于我们平时用的android APP。最终用户可以直接与之交互(可以点击屏幕,查看结果等)。而服务类型是不能直接交互的。如手机来电监测就是一种服务,你看不到它,但它一直在运行,否则我们接不到电话。
typedef enum {
/** Unknown */
UNKNOWN = 0,
/** Page */
PAGE,
/** Service */
SERVICE
} AbilityType;
APP状态
下表描述了各种APP状态
状态 | 描述 |
STATE_UNINITIALIZED | APP刚创建,还未初始化好 |
STATE_INITIAL | APP已初始化好,还未做好服务准备 |
STATE_INACTIVE | APP已准备好了,但不能交互(用于SERVICE) |
STATE_ACTIVE | APP已准备好了,可以交互了(用于PAGE) |
STATE_BACKGROUND | APP已就绪,但跑到后台去了,暂时不能交互(用于PAGE) |
这里补充一下后台的概念。直观理解就是这个APP的界面被另外的APP遮盖住了,无法再看见。所以藏在后面了,称为处于后台。而我们肉眼看到的这个APP称为处于前台。
APP窗口
对于PAGE类型APP。 则具有APP窗口对象,SERVICE类型APP, 则不具有窗口对象。
APP窗口切片
每个APP窗口可能还含有若干窗口切片,每个窗口切片关联独立的UI视图。窗口切片组合可以带来更高等级的用户体验。只有原生APP(C++编写的)支持窗口切片。非原生APP(JS 编写)不支持。
在支持窗口切片的APP中,每个切片的状态独立演化,看起来就像是一个独立的APP。实际是所有切片组合成一个APP。
路由信息
在支持窗口切片的APP中,窗口切片之间的跳转称为切片路由。每个APP有一个主路由,描述呈现给用户的主页面。然后有一个路由表,描述如何进行跳转。路由信息均由APP开发人员设定。
APP包名
每个APP有一个软件包名,如android系统的xyz.apk。鸿蒙也有类似的包名。
APP安装路径
每个APP安装到设备,实际上是放入某个知名路径下,如/software/installdir 。
APP数据存取路径
APP运行过程中产生的数据存放于此目录下,如/xyz/data 。
APP窗口切片栈
在支持窗口切片的APP中,为了支持统一的返回语义,不同的窗口以栈的形式组织在一起,这样,通过点击返回就可以不断回到以前的窗口切片。
APP之间的交互模型
由于APP分为有界面的PAGE和无界面的SERVICE。因此交互模型也分为4类。
源APP | 目标APP | 交互方式 |
PAGE | PAGE | 启动PAGE APP,新APP运行在前台,源APP隐藏在后台 |
PAGE | SERVICE | 分为2种情况: 1. service还未启动,则启动service,如当前在操作某手机APP的过程中,开启手机屏幕录像功能 2. service已启动,则将page绑定到service(内部术语是connect)。如屏幕录像功能正在运行,然后在手机上再启动一个page APP。 |
SERVICE | PAGE | 理论上可行,一般使用上述第一种方式达成目标 |
SERVICE | SERVICE | 理论上可行,但大多数情况下可以将2个SERVICE合并成同一个SERVICE。或者通过将一个PAGE connect到2个SERVICE来实现 |
注意。SERVICE虽然可以独立存活,但最终用户只能与PAGE类型APP交互。所以,一个没有connect到任何PAGE的SERVICE是没有存在价值的。一个SERVICE可以被多个PAGE来connect。一个PAGE也可以connect到多个SERVICE。PAGE和SERVICE之间的关系是多对多的。
与管理进程的关系
管理进程类似于租房中介一样,房东与租客初期都和中介沟通。当签订租房合同后,房东与房客就直接沟通了,不再管租房中介。但租房中介有它们的联系方式,所以随时在关注它们的状态:房屋是否又空出来了?是否又需要租房了?。
类似的。APP管理进程关注着APP进程的一举一动。APP进程会将自己的一些重要信息同步到APP管理进程。
APP管理进程了解所有APP进程的重要情况,为APP的正常稳定运行发挥着重要的作用。
APP进程间的交互
APP进程启动前
APP进程启动前,总有一个条件触发APP的启动。可以是其他APP(如PAGE启动PAGE,PAGE启动SERVICE)。或者其他进程(如某进程启动系统第一个APP)。
不管哪种情况,APP的启动都是调用APP管理进程的服务来开始实施的。如下代码。
int StartAbility(const Want *want)
{
if (want == nullptr) {
HILOG_ERROR(HILOG_MODULE_APP, "want is null, StartAbility failed");
return -1;
}
//初始化APP管理进程的客户端代理
OHOS::AbilityMsClient::GetInstance().Initialize();
//向管理进程发消息,请求管理进程启动我们想启动的APP(want描述)
return OHOS::AbilityMsClient::GetInstance().ScheduleAms(want, 0, nullptr, START_ABILITY);
}
want信息描述如下
typedef struct {
/**
* 内部包含程序包的名称,类似于android的xyz.apk.
* 其它字段暂不解释
*/
ElementName *element;
#ifdef OHOS_APPEXECFWK_BMS_BUNDLEMANAGER
/**
*
* 记录本端的通信ID, 当对端发送应答时,可以通过这个通信ID发送回来
*/
SvcIdentity *sid;
#endif
/**
* 自定义数据
*/
void *data; //
/**
* 自定义数据长度
*/
uint16_t dataLength;
} Want;
然后APP管理进程进行适当的处理
//APP管理进程处理APP启动请求的入口函数
int32 AbilityMgrFeature::StartAbilityInvoke(const void *origin, IpcIo *req)
{
pid_t uid = GetCallingUid(origin); //发起请求的用户ID
if (uid < 0) {
PRINTE("AbilityMgrFeature", "invalid uid argument");
return EC_INVALID;
}
Want want = { nullptr, nullptr, nullptr, 0 };
if (!DeserializeWant(&want, req)) { //从消息中取出请求启动哪个APP
return EC_FAILURE;
}
int retVal = StartAbilityInner(&want, uid); //具体处理过程
ClearWant(&want);
return retVal;
}
在APP管理进程内部具体处理过程中,会判断APP进程是否存在。不存在则创建进程,并记录了下来(appRecord_)。否则只需要激活APP。
AbilityMsStatus PageAbilityRecord::StartAbility()
{
if (appRecord_ == nullptr) {
// If process is not exist, start process.
//创建APP进程
appRecord_ = AppManager::GetInstance().StartAppProcess(bundleInfo_);
if (appRecord_ == nullptr) {
return AbilityMsStatus::ProcessStatus("start app process fail");
}
appRecord_->SetPendingAbility(this);
return AbilityMsStatus::Ok();
}
return ActiveAbility(); //激活APP
}
当成功创建并激活APP进程后,还要给原来发请求的进程回应
//由管理进程回应请求方APP启动结果
void AbilityMgrHandler::StartAbilityCallback(const Want *want, int code)
{
if ((want == nullptr) || (want->sid == nullptr) || (code == EC_SUCCESS)) {
return;
}
PRINTI("AbilityMgrHandler", "start ability failed callback");
IpcIo io;
char data[IPC_IO_DATA_MAX];
IpcIoInit(&io, data, IPC_IO_DATA_MAX, 0);
if (!SerializeElement(&io, want->element)) {
return;
}
IpcIoPushInt32(&io, code); //返回值在code中,放入消息里面
//向want->sid发送回去这个应答消息,want->sid即原请求发送方通信地址
if (Transact(nullptr, *(want->sid), SCHEDULER_APP_INIT, &io, nullptr, LITEIPC_FLAG_ONEWAY, nullptr) != LITEIPC_OK) {
PRINTE("AbilityMgrHandler", "start ability callback failed, ipc error");
}
}
所以,APP管理进程记录了所有已启动的APP进程---通过appRecord
APP进程启动时
APP进程刚启动时,只有APP ID,无其它数据和功能。在执行正常的APP功能代码前,先建立与APP管理进程之间的通信通道,并通知管理进程,我要执行哪个APP程序包。如下代码
//将APP绑定到一个具体的APP程序包
void AbilityThread::AttachBundle(uint64_t token)
{
eventHandler_ = new AbilityEventHandler();
abilityScheduler_ = new AbilityScheduler(*eventHandler_, *this);
//初始化APP管理进程的客户端代理,建立同步通信通道
//通过此通道,可以远程调用APP管理进程的服务,并可同步获得结果
if (!AbilityMsClient::GetInstance().Initialize()) {
HILOG_ERROR(HILOG_MODULE_APP, "ams feature is null");
return;
}
//紧接着在创建一个异步通信通道,此通道用于接收其他进程主动发来的消息
//先申请一个异步通道的ID变量空间
identity_ = static_cast<SvcIdentity *>(AdapterMalloc(sizeof(SvcIdentity)));
if (identity_ == nullptr) {
HILOG_ERROR(HILOG_MODULE_APP, "ams identity is null");
return;
}
//然后注册回调函数AbilityScheduler::AmsCallback,并获得异步通道的ID
//此异步通道一般是一个有别于主线程的额外线程,再加上一个消息处理函数标识。
//其他进程向此ID发送消息后,本APP进程接收到消息,会调用此回调函数
//目前的设计是,只有APP管理进程会获得这个ID,因此只有APP管理进程会发送消息
//给AmsCallback函数接收---- AMS意即APP Manager Service。
//AmsCallback即从AMS进程调用回来
int32_t ret = RegisterIpcCallback(AbilityScheduler::AmsCallback, 0, IPC_WAIT_FOREVER, identity_, abilityScheduler_);
if (ret != 0) {
HILOG_ERROR(HILOG_MODULE_APP, "RegisterIpcCallback failed");
AdapterFree(identity_);
return;
}
//通过客户端请求APP服务进程处理APP与程序包的绑定,APP服务进程可能会拒绝这个绑定,也可能同意这个绑定
//同时APP服务进程会记录下此APP的异步通道ID(identity_),这样,APP服务进程主动向APP进程发消息的时候,就没有问题了。
//对于APP服务进程直接向APP进程返回服务应答的情况(直接从通信消息中就能知道如何应答,这个通信过程隐藏中客户端代理和服务器代理的设计中,本处不再描述)。
AbilityMsClient::GetInstance().ScheduleAms(nullptr, token, identity_, ATTACH_BUNDLE);
}
APP管理进程收到APP进程的程序包绑定请求,做如下的主要处理
//入口消息处理函数, 不赘述
int32 AbilityMgrFeature::AttachBundleInvoke(const void *origin, IpcIo *req);
//将APP进程绑定到程序包
AbilityMsStatus AbilityAttachTask::Execute()
{
PRINTD("AbilityAttachTask", "start");
if (client_ == nullptr) {
return AbilityMsStatus::TaskStatus("Attach", "invalid argument");
}
// step1: Get app record by token.
// 此时APP进程应该已经存在了
auto appRecord = const_cast<AppRecord *>(AppManager::GetInstance().GetAppRecordByToken(
client_->GetToken(), client_->GetPid()));
if (appRecord == nullptr) {
return AbilityMsStatus::TaskStatus("Attach", "appRecord not found");
}
// step2: Save ability thread client.
//为每个APP进程在管理进程中配置一个客户端(负责APP进程的异常重启)
AbilityMsStatus status = appRecord->SetAbilityThreadClient(*client_);
CHECK_RESULT(status);
// step3: Load permission
//判断用户是否有运行此APP的权限
status = appRecord->LoadPermission();
if (!status.IsOk()) {
//没有权限则放弃绑定程序包,并删除APP进程记录
AppManager::GetInstance().RemoveAppRecord(*appRecord);
return status; // 返回错误后,APP进程后面会退出
}
// step4: Init app
//向APP进程发送消息,由APP进程自身完成初始化,等待初始化完成
status = appRecord->AppInitTransaction();
CHECK_RESULT(status); //如果初始化失败,也返回失败,APP进程后面会退出
// step5: Launch pending ability.
//向APP异步发送消息,要求激活服务或页面
return appRecord->LaunchPendingAbility();
}
APP进程的初始化
前面提过,在APP进程启动过程中,会向APP管理进程发送异步消息(绑定程序包). APP管理进程会再发送消息给APP进程,要求其进行初始化动作,并等待初始化完成。
下面描述APP进程端的处理逻辑。
//接收来自APP管理进程的APP初始化请求
AbilityScheduler::AmsCallback
SCHEDULER_APP_INIT //请求消息的类型为这个
//APP初始化涉及的其它函数有
AbilityScheduler::PerformAppInit
AbilityThread::PerformAppInit
在APP进程中初始化完APP后,APP的安装路径,数据存放位置等都设置好了,依赖的各种共享库也已打开(dl_open)。
APP进程的页面或服务激活
前面提过,当APP进程初始化完成以后,APP管理进程还会激活APP。即对于SERVICE,需要将APP激活到INACTIVE状态。对于PAGE,需要激活到ACTIVE状态。
//这里是APP管理进程端代码
AbilityMsStatus AppRecord::LaunchPendingAbility()
{
if (pendingAbilityRecord_ != nullptr) {
AbilityMsStatus status;
if (pendingAbilityRecord_->GetAbilityInfo().abilityType == AbilityType::SERVICE) {
status = pendingAbilityRecord_->InactiveAbility(); //激活服务APP
} else {
status = pendingAbilityRecord_->ActiveAbility(); //激活页面APP
}
// MissionRecord release AbilityRecord
pendingAbilityRecord_ = nullptr;
return status;
}
return AbilityMsStatus::LifeCycleStatus("pending ability not exist");
}
APP的状态转换有一个术语,称为生命周期变化。在APP进程中,当AmsCallback收到消息后,会进行生命周期变化处理(SCHEDULER_ABILITY_LIFECYCLE)
实际进行生命周期状态变化处理的过程稍显复杂,如下。
//执行APP状态转换
void AbilityThread::PerformTransactAbilityState(const Want &want, int state, uint64_t token, int abilityType)
{
HILOG_INFO(HILOG_MODULE_APP, "perform transact ability state to [%{public}d]", state);
Ability *ability = nullptr; //此结构为APP进程内对APP的详细描述的信息
auto iter = abilities_.find(token); //根据APP ID查找Ability结构
if ((iter == abilities_.end()) || (iter->second == nullptr)) {
// APP不存在
if (want.element == nullptr) { //APP名称信息为空
HILOG_ERROR(HILOG_MODULE_APP, "element name is null, fail to load ability");
AbilityMsClient::GetInstance().SchedulerLifecycleDone(token, STATE_INITIAL);
return;
}
//APP名称
auto abilityName = isNativeApp_ ? want.element->abilityName : ACE_ABILITY_NAME;
//根据名称创建ability对象
ability = AbilityLoader::GetInstance().GetAbilityByName(abilityName);
if (ability == nullptr) {
HILOG_ERROR(HILOG_MODULE_APP, "fail to load ability: %{public}s", abilityName);
AbilityMsClient::GetInstance().SchedulerLifecycleDone(token, STATE_INITIAL);
return;
}
HILOG_INFO(HILOG_MODULE_APP, "Create ability success [%{public}s]", want.element->abilityName);
// Only page ability need to init display
#ifdef ABILITY_WINDOW_SUPPORT
if (abilityType == PAGE) {
InitUITaskEnv(); //如果是PAGE类的APP,还得创建UI线程
}
#endif
//记录APP的相关信息
ability->Init(token, abilityType, AbilityThread::isNativeApp_);
abilities_[token] = ability; //记录新创建的APP
} else {
ability = iter->second; //Ability已存在
}
if (ability->GetState() != state) {
//需要更新状态的情况,则处理状态转换
HandleLifecycleTransaction(*ability, want, state);
}
HILOG_INFO(HILOG_MODULE_APP, "perform transact ability state done [%{public}d]", ability->GetState());
//通知APP管理进程本次生命周期状态变化结束
AbilityMsClient::GetInstance().SchedulerLifecycleDone(token, ability->GetState());
if (ability->GetState() == STATE_ACTIVE) {
StartAbilityCallback(want); //PAGE启动PAGE的情况,回应启动成功
}
if (ability->GetState() == STATE_INITIAL) {
abilities_.erase(token); //APP已停止服务,即将删除
delete ability; //释放APP描述信息
}
}
PAGE和SERVICE的连接和断开
PAGE和SERVICE是支持多对多连接的。即一个PAGE可以连接多个SERVICE。一个SERVICE也可以被多个PAGE来连接使用。代码中涉及此类关系的时候会稍作注解。
由于断开过程和连接过程是互逆的。所以,本处只解释连接过程,断开过程不描述,请自行阅读代码。
一、首先由PAGE发起连接请求
//请求连接service (由token标识),向APP管理进程发送请求
int AbilityServiceManager::ConnectAbility(const Want &want, const IAbilityConnection &conn, uint64_t token, void *storeArg)
{
if (conn.OnAbilityDisconnectDone == nullptr || conn.OnAbilityConnectDone == nullptr) {
HILOG_INFO(HILOG_MODULE_APP, "IAbilityConnection callback func is null");
return LITEIPC_EINVAL;
}
//需要和每个Service之间建立通信通道
//这里创建并管理通信通道集合
StoreArgs *storeArgs = AddStoreArgs(conn, storeArg);
if (storeArgs == nullptr) {
return LITEIPC_EINVAL;
}
//生成并设置通信通道,本service的通信ID为storeArgs->sid(输出参数)
//ConnectAbilityCallBack从通信通道接收消息
int32_t result = RegisterIpcCallback(ConnectAbilityCallBack, 0, IPC_WAIT_FOREVER, storeArgs->sid, storeArgs);
if (result != LITEIPC_OK) {
RemoveStoreArgs(nullptr, storeArgs);
delete storeArgs->sid;
delete storeArgs;
return result;
}
AbilityMsClient::GetInstance().Initialize();
//向APP管理进程发起请求连接service (token, want)
//并注册本端通信通道storeArgs->sid
result = AbilityMsClient::GetInstance().ScheduleAms(&want, token, storeArgs->sid, CONNECT_ABILITY);
if (result != EC_SUCCESS) {
UnregisterIpcCallback(*(storeArgs->sid));
RemoveStoreArgs(nullptr, storeArgs);
delete storeArgs->sid;
delete storeArgs;
}
return result;
}
在APP管理进程内,主要的处理连接请求的代码如下。一些如Service当前不存在,需要先启动再连接等逻辑则不再赘述,这里只描述最主要的逻辑。
AbilityMsStatus AbilityConnectTask::PerformConnectTask(PageAbilityRecord *service)
{
ConnectStatus serviceConnectStatus = service->GetConnectStatus();
if (serviceConnectStatus == ConnectStatus::DISCONNECTING || serviceConnectStatus == ConnectStatus::DISCONNECT ||
serviceConnectStatus == ConnectStatus::STOPPING || serviceConnectStatus == ConnectStatus::STOPPED) {
PRINTW("AbilityConnectTask", "service status has disconnected");
return AbilityMsStatus::TaskStatus("AbilityConnectTask", "service status has disconnected");
}
auto connectRecord = service->GetConnectRecord(connectSid_);
if (connectRecord == nullptr) {
//新建发起请求的PAGE记录
connectRecord = new AbilityConnectRecord(connectSid_, token_);
connectRecord->SetStatus(ConnectStatus::CONNECTING); //已经接纳与此PAGE的连接
service->PushConnectRecord(connectRecord); //加入连接此Service的PAGE列表
}
AbilityMsStatus abilityMsStatus = AbilityMsStatus::Ok();
switch (serviceConnectStatus) {
case ConnectStatus::INIT: {
if (service->GetCurrentState() == STATE_UNINITIALIZED) {
//SERVICE还未初始化完,设置成等待连接状态,各PAGE进程需要阻塞等待
PRINTI("AbilityConnectTask", "service status is initializing, waiting connecting");
service->SetConnectStatus(ConnectStatus::WAIT_CONNECT);
} else {
//第一个连接此Service的PAGE到来,设置成连接协商状态
PRINTI("AbilityConnectTask", "service status is init, connection is connecting");
service->SetConnectStatus(ConnectStatus::CONNECTING);
abilityMsStatus = service->ConnectAbility(); //转发连接请求到service
}
break;
}
case ConnectStatus::CONNECTED:
{
if (connectRecord->GetStatus() == ConnectStatus::CONNECTING) {
//之前已经有PAGE连接过我了,那么直接回复PAGE,SERVICE可用,连接成功
PRINTI("AbilityConnectTask", "service status has connected, connection is connecting");
abilityMsStatus = service->ConnectAbilityDone(); //回复PAGE service已成功连接,这里的关键是APP管理进程有保存service的通信ID,
//所以可以告诉PAGE。后面PAGE直接使用SERVICE的通信ID与SERVICE通信
} else {
//PAGE发送的重复连接请求
PRINTI("AbilityConnectTask", "connection has connected");
}
break;
}
default:
break;
}
return abilityMsStatus;
}
目标Service收到连接请求的处理逻辑如下
//Service收到PAGE发来的连接请求时的处理
//当然这个请求是由APP管理进程转发而来
const SvcIdentity *Ability::OnConnect(const Want &want)
{
HILOG_INFO(HILOG_MODULE_APP, "Ability Connect");
sid_ = static_cast<SvcIdentity *>(AdapterMalloc(sizeof(SvcIdentity)));
if (sid_ == nullptr) {
HILOG_ERROR(HILOG_MODULE_APP, "malloc memory error, sid_ is null");
return nullptr;
}
//注册一个异步通信通道,用来接收PAGE和本SERVICE连接成功后,由PAGE再次发来的请求
//通信通道的ID为sid_(输出参数)
//此通信通道的消息入口函数为Ability::MsgHandleInner
//这个函数返回后,后面的函数会把通信通道同步到APP管理进程。
//这样,其它PAGE要连接我这个SERVICE时,直接从APP管理进程获取此通信通道(视为连接成功)。
int32_t ret = RegisterIpcCallback(Ability::MsgHandleInner, 0, IPC_WAIT_FOREVER, sid_, this);
if (ret != 0) {
HILOG_ERROR(HILOG_MODULE_APP, "register ipc callback error, ret is %{public}d", ret);
AdapterFree(sid_);
sid_ = nullptr;
return nullptr;
}
return sid_;
}
//处理APP管理进程转发来的连接请求
void AbilityThread::PerformConnectAbility(const Want &want, uint64_t token)
{
auto iter = abilities_.find(token);
if (iter == abilities_.end() || iter->second == nullptr) {
HILOG_ERROR(HILOG_MODULE_APP, "app has been stopped"); //被连接的APP不存在
return;
}
//实际上调用的是上一个函数,返回的是新创建的通信通道ID
const SvcIdentity *sid = iter->second->OnConnect(want); //处理连接消息
//将新创建的通信通道ID同步给APP管理进程
AbilityMsClient::GetInstance().ScheduleAms(nullptr, token, sid, CONNECT_ABILITY_DONE);
}
如上所述,当目标service进程处理完连接请求后,向APP管理进程回应连接成功,则APP管理进程还应该将此成功消息转发给PAGE进程。
AbilityMsStatus AbilityConnectDoneTask::Execute()
{
PRINTD("AbilityConnectDoneTask", "start");
if (abilityMgrContext_ == nullptr) {
return AbilityMsStatus::TaskStatus("connectTaskDone", "invalid argument");
}
AbilityStackManager &stackManager = AbilityStackManager::GetInstance();
PageAbilityRecord *service = stackManager.FindServiceAbility(*abilityMgrContext_, token_);
if (service == nullptr) {
return AbilityMsStatus::TaskStatus("connectTaskDone", "service ability dose not exists");
}
if (service->IsPerformStop()) {
return AbilityMsStatus::TaskStatus("connectTaskDone", "service is stopping");
}
service->SetServiceSid(serviceSid_); //将service进程新生成的通信ID记录在管理进程中
AbilityMsStatus status = service->ConnectAbilityDone(); //并通知PAGE进程连接成功消息
service->SetConnectStatus(ConnectStatus::CONNECTED); //将SERVICE进程状态修改成已被PAGE连接状态
return status;
}
//这里注意一个细节,在APP管理进程向APP进程(PAGE)反馈Service已成功连接的消息时
//向多个等待连接此Service的PAGE进程广播这一消息
//这样,所有连接此service的APP都会收到连接成功消息
AbilityMsStatus PageAbilityRecord::ConnectAbilityDone()
{
if (appRecord_ == nullptr) {
return AbilityMsStatus::TaskStatus("connectAbilityDone, ", "app record not exist");
}
for (auto record : connectRecords_) { //通知所有其它正在连接的APP,本service已成功连接
if (record != nullptr && record->GetStatus() == ConnectStatus::CONNECTING) {
record->SetStatus(ConnectStatus::CONNECTED); //APP管理进程内提前记录下PAGE与SERVICE的连接状态
appRecord_->ConnectDoneTransaction(want_, serviceSid_, record->GetConnectSid()); //通知PAGE进程更新连接状态(连接成功).
}
}
return AbilityMsStatus::Ok();
}