鸿蒙APP框架分析

目录

鸿蒙APP

APP框架代码组成

APP运行框架代码

APP管理进程代码

APP基本概念

token

APP类型

APP状态

APP窗口

APP窗口切片

路由信息

APP包名

APP安装路径

APP数据存取路径

APP窗口切片栈

APP之间的交互模型

与管理进程的关系

APP进程间的交互

APP进程启动前

APP进程启动时

APP进程的初始化

APP进程的页面或服务激活

PAGE和SERVICE的连接和断开


鸿蒙APP

APP框架代码组成

目录描述
foundation/aafwkAPP运行框架代码
foundation/aafwk/frameworks普通APP进程框架代码,运行于每个普通APP进程实例中
foundation/aafwk/servicesAPP管理进程代码,管理所有普通的APP进程,是所有APP进程信息汇总的地方
foundation/aafwk/interfacesAPP运行框架代码头文件集散地

 

APP运行框架代码

app运行框架代码作用于每个普通APP进程中。普通APP进程和APP管理进程之间联系紧密,因此运行框架代码中有一部分主要负责与管理进程之间的交互逻辑,另一部分是常规其他逻辑。

目录描述
foundation/aafwk/frameworks/abilitymgr_lite与管理进程交互逻辑部分
foundation/aafwk/frameworks/ability_lite其它正常逻辑
foundation/aafwk/frameworks/want_litewant结构为APP和APP管理进程都会用到的数据结构,主要用于传递进程间的数据,本目录主要实现want的序列化和反序列化以及set和get

 

APP管理进程代码

本部分代码主要实现APP管理进程的逻辑,此进程存在的目的就是为了支持其它普通APP进程的正常运行。另外,本部分代码还包含了一个基本的测试代码,此测试代码使用shell(命令行)启动,为一个独立的进程,用来测试APP管理进程和普通APP进程的基本功能(可以认为是一种冒烟测试)。

目录描述
foundation/aafwk/services/abilitymgr_lite/srcAPP管理进程实现代码
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状态

APP状态语义
状态描述
STATE_UNINITIALIZEDAPP刚创建,还未初始化好
STATE_INITIALAPP已初始化好,还未做好服务准备
STATE_INACTIVEAPP已准备好了,但不能交互(用于SERVICE)
STATE_ACTIVEAPP已准备好了,可以交互了(用于PAGE)
STATE_BACKGROUNDAPP已就绪,但跑到后台去了,暂时不能交互(用于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交互方式
PAGEPAGE启动PAGE APP,新APP运行在前台,源APP隐藏在后台
PAGESERVICE

分为2种情况:

1. service还未启动,则启动service,如当前在操作某手机APP的过程中,开启手机屏幕录像功能

2. service已启动,则将page绑定到service(内部术语是connect)。如屏幕录像功能正在运行,然后在手机上再启动一个page APP。

SERVICEPAGE理论上可行,一般使用上述第一种方式达成目标
SERVICESERVICE理论上可行,但大多数情况下可以将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();
}

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值