和按需启动一样,OpenHarmony的系统服务(System Ability)同样支持按需卸载,可以配置的卸载条件同样有:

  • 设备上下线 如 deviceonline: on|off|ready
  • 设置开关 如 wifi_status:on|off
  • 系统参数
  • 系统公共事件
  • 定时任务

​ 配置方法与按需启动一样,唯一的区别是启动条件是"start-on-demand",而卸载条件是"stop-on-demand",这里不再赘述,可以参考https://laval.csdn.net/66e2dc55cd8b2677c3bddc9c.html,事实上多数情况下,我们需要在服务长期处于空闲状态时卸载任务,这不像由特定的事件或者开关状态来触发卸载,是一种动态的卸载方式,我们还是以updater_sa作为例子来了解这种卸载方式是如何实现的。

img

​ 如上图的流程,在updater_sa服务启动时会启动一个STARTUP_LOOPER_INTERVAL间隔的循环定时器:

// base/update/updateservice/services/startup/manage/src/startup_manager.cpp
void StartupManager::IdleLoop()
{
    ENGINE_LOGI("IdleLoop");
    DelayedSingleton<StartupSchedule>::GetInstance()->RegisterLooper([=]() {
        // 如果服务不是空闲状态直接返回
        if (!IdleCheck()) {
            ENGINE_LOGD("IdleLoop not idle");
            return;
        }
        // 如果已在退出流程中也直接返回
        if (!PreExit()) {
            ENGINE_LOGI("IdleLoop pre exit fail");
            return;
        }
        // 否则判定为长时间空闲,卸载服务
        SAExit();
    });
}

bool StartupManager::IdleCheck()
{
    if (scheduleManager_ == nullptr) {
        ENGINE_LOGE("IdleCheck scheduleManager is null, return idle");
        return true;
    }

    if (abs(TimeUtils::GetTimestamp() - lastIdleCheckTime_) <
        static_cast<int64_t>(ScheduleConfig::GetIdleCheckInterval())) {
        ENGINE_LOGD("IdleCheck check time not arrive: lastIdleCheckTime is %{public}s",
            TimeUtils::GetPrintTimeStr(lastIdleCheckTime_).c_str());
        return false;
    }

    lastIdleCheckTime_ = TimeUtils::GetTimestamp();
    ENGINE_LOGI(
        "IdleCheck update lastIdleCheckTime: %{public}s", TimeUtils::GetPrintTimeStr(lastIdleCheckTime_).c_str());

    bool isIdle = scheduleManager_->IdleCheck();
    ENGINE_LOGI("IdleCheck idleState is %{public}s", StringUtils::GetBoolStr(isIdle).c_str());
    return isIdle;
}

bool StartupManager::PreExit()
{
    if (scheduleManager_ == nullptr) {
        ENGINE_LOGE("IdleCheck PreExit is null, return true");
        return true;
    }
    return scheduleManager_->Exit();
}

void StartupManager::SAExit() const
{
    ENGINE_LOGI("SAExit");
    sptr<ISystemAbilityManager> sm = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (sm == nullptr) {
        ENGINE_LOGE("GetSystemAbilityManager samgr object null!");
        return;
    }
    int32_t res = sm->UnloadSystemAbility(UPDATE_DISTRIBUTED_SERVICE_ID);
    ENGINE_LOGI("UnloadSystemAbility res is %{public}d", res);
}

​ 分析StartupManager::IdleCheck函数,我们发现最终是根据AccessManager::accessMap_容器的内容及AccessManager::isRemoteIdle_来判定服务是否空闲的。

// base/update/updateservice/services/startup/manage/src/schedule_manager.cpp
bool ScheduleManager::IdleCheck()
{
    if (accessManager_ != nullptr && !accessManager_->IsIdle()) {
        return false;
    }
    ENGINE_LOGI("IdleCheck true");
    TaskManage(scheduleTask_);
    return true;
}

base/update/updateservice/services/startup/access/src/access_manager.cpp
bool AccessManager::IsIdle()
{
    if (!isRemoteIdle_) {
        ENGINE_LOGI("AccessManager remote not idle");
        return false;
    }

    std::lock_guard<std::recursive_mutex> guard(mutex_);
    for (auto &[type, access] : accessMap_) {
        if (access != nullptr && !access->IsIdle()) {
            return false;
        }
    }
    return true;
}

void AccessManager::SetRemoteIdle(bool isRemoteIdle)
{
    ENGINE_LOGI("AccessManager SetRemoteIdle %{public}s", StringUtils::GetBoolStr(isRemoteIdle).c_str());
    isRemoteIdle_ = isRemoteIdle;
}

​ 继续追踪代码,我们发现在执行升级操作或者对外接口被调用时都会通过UpdateService::RegisterUpdateCallback函数将isRemoteIdle_状态置为空。

// base/update/updateservice/services/engine/src/update_service.cpp
int32_t UpdateService::RegisterUpdateCallback(const UpgradeInfo &info, const sptr<IUpdateCallback> &updateCallback)
{
    ENGINE_LOGI("RegisterUpdateCallback");
    UnregisterUpdateCallback(info);
    {
        std::lock_guard<std::mutex> lock(clientProxyMapLock_);
        ClientProxy clientProxy(info, updateCallback);
        clientProxy.AddDeathRecipient();
        clientProxyMap_.insert({info, clientProxy});
    }
    if (!info.IsLocal()) {
        UpdateServiceCache::SetUpgradeInfo(info);
    }
    DelayedSingleton<AccessManager>::GetInstance()->SetRemoteIdle(clientProxyMap_.empty());
    return INT_CALL_SUCCESS;
}

// base/update/updateservice/frameworks/js/napi/update/src/local_updater.cpp
void LocalUpdater::Init()
{
    PARAM_CHECK(!isInit_, return, "local updater has init.");
    UpdateCallbackInfo callback {
        [this](const EventInfo &eventInfo) {
            NotifyEventInfo(eventInfo);
        },
    };
    ENGINE_LOGI("LocalUpdater::Init");
    UpgradeInfo upgradeInfo;
    upgradeInfo.upgradeApp = LOCAL_UPGRADE_INFO;
    UpdateServiceKits::GetInstance().RegisterUpdateCallback(upgradeInfo, callback);
    isInit_ = true;
}

// base/update/updateservice/frameworks/js/napi/update/src/update_client.cpp
void UpdateClient::RegisterCallback()
{
    ENGINE_LOGI("RegisterCallback");
    UpdateCallbackInfo callback{ [=](const EventInfo &eventInfo) { NotifyEventInfo(eventInfo); } };
    constexpr int32_t sleepDuration = 10; // 回调注册失败后再次尝试之前的等待时间,单位:毫秒
    constexpr int32_t maxRetryTimes = 5;  // 回调注册失败最大尝试次数
    int32_t retryTimes = 0;
    do {
        int32_t ret = UpdateServiceKits::GetInstance().RegisterUpdateCallback(upgradeInfo_, callback);
        if (ret == INT_CALL_SUCCESS) {
            break;
        }

        if (retryTimes++ < maxRetryTimes) {
            ENGINE_LOGI("InitCallback fail, will retry after %{public}d milliseconds", sleepDuration);
            std::this_thread::sleep_for(std::chrono::milliseconds(sleepDuration));
        } else {
            ENGINE_LOGE("InitCallback fail after retry %{public}d times", retryTimes);
        }
    } while (retryTimes < maxRetryTimes);
}

// base/update/updateservice/services/engine/src/update_service_stub.cpp
int32_t UpdateServiceStub::RegisterUpdateCallbackStub(UpdateServiceStubPtr service,
    MessageParcel& data, MessageParcel& reply, MessageOption &option)
{
    ENGINE_LOGI("RegisterUpdateCallbackStub");
    RETURN_FAIL_WHEN_SERVICE_NULL(service);
    UpgradeInfo upgradeInfo {};
    MessageParcelHelper::ReadUpgradeInfo(data, upgradeInfo);
    auto remote = data.ReadRemoteObject();
    ENGINE_CHECK(remote != nullptr, return INT_CALL_IPC_ERR, "Failed to read remote obj");
    return service->RegisterUpdateCallback(upgradeInfo, iface_cast<IUpdateCallback>(remote));
}

​ 至此,updater_sa服务的空闲卸载逻辑就比较清晰了:启用一个周期定时器来检测服务空闲状态变量,并通过记录的服务进入空闲状态的时间来判定服务是否长时间处于空闲状态,进而使用SA管理服务的接口UnloadSystemAbility来卸载服务。而在我们使用设计服务的接口或者启动本地或者远程升级流程时会将这个空间状态变量设置为false。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐