1 关键字

Launcher;RecentView;missionInfo;mission_list_manager

 

2 问题描述

问题现象:Launcher获取recentView时顺序错误,不是应用打开顺序,出现几率30%。

运行环境:硬件 dayu200,软件:3.1release

测试步骤:

  1. 打开browser,看到界面后,点击“Home”或者“Back”键。

  2. 打开fileManager,看到界面后,点击“Home”或者“Back”键。

  3. 打开settings,看到界面后,点击“RecentView“,进入RecentView窗口。

  4. 依次循环操作,有30%几率获取结果为文件管理,设置,浏览器(顺序错误),另外70%获取结果为设置,文件管理,浏览器(顺序正确)。

 

3 问题原因

3.1 正常机制

  • 应用拉起时,mission_list_manager会记录当前任务信息missionInfo至任务列表;

  • 应用最小化到后台时,mission_list_manager会更新任务列表中当前任务的时间为当前时间;

  • RecentView窗口中应用顺序和最近操作顺序一致。

 

3.2 异常机制

  • 当应用最小化后,迅速拉起另一个应用(1秒内完成操作),大概率出现RecentView窗口中应用顺序和最近操作顺序不一致。

  • 拉起应用时,新增任务信息missionInfo至任务列表,新增过程中会拿任务列表元素的时间进行判断,从前往后循环,当元素的时间小于当前要插入任务的时间时,当前位置即为需要插入的位置,该逻辑可保证任务列表数据的有序性,即时间从大到小排列(也就是按照最近操作的顺序排列),但是有一种特殊场景,由于记录的时间的精度是到秒的,并不是到毫秒的,所以同一秒内的任务列表新增会有问题,因为时间是等于,所以本来应该插入列表第1位的,实际会跳过第1位的等于插入到第2位,造成和最近操作的顺序不一致。

源码如下:

bool MissionInfoMgr::AddMissionInfo(const InnerMissionInfo &missionInfo)
{
    auto id = missionInfo.missionInfo.id;
    if (missionIdMap_.find(id) != missionIdMap_.end() && missionIdMap_[id]) {
        HILOG_ERROR("add mission info failed, missionId %{public}d already exists", id);
        return false;
    }
​
    auto listIter = missionInfoList_.begin();
    for (; listIter != missionInfoList_.end(); listIter++) {
        if (listIter->missionInfo.time < missionInfo.missionInfo.time) {
            break;  // first listIter->time < missionInfo.time
        }
    }
​
    if (!taskDataPersistenceMgr_->SaveMissionInfo(missionInfo)) {
        HILOG_ERROR("save mission info failed");
        return false;
    }
​
    missionInfoList_.insert(listIter, missionInfo);
    missionIdMap_[id] = true;
    return true;
}

 

4 解决方案

  • 将api中的小于改成小于等于,避免出现同秒操作导致的顺序不一致

  • 将missionInfo的时间精度调整到毫秒,记录毫秒数,以保证时间的绝对有序性

     

5 定位过程

该问题为特殊场景操作导致的运行异常:

  • 根据问题现状进行初步分析:RecentView窗口中应用顺序有误,那么api调用获取的应用顺序应该有误,直接打印相关信息,通过日志进行确认:

    async getRecentMissionsList(): Promise<RecentMissionInfo[]> {
      const recentMissionsList = new Array<RecentMissionInfo>();
      let listData = new Array();
      await missionManager.getMissionInfos('', AmsMissionManager.RECENT_MISSIONS_LIMIT_NUM)
        .then((res) => {
          Log.showInfo(TAG, `getRecentMissionsList res: ${JSON.stringify(res)}`);
          Log.showInfo(TAG, `getRecentMissionsList res.length: ${res.length}`);
          listData = res;
        })
        .catch((err) => {
          Log.showError(TAG, `getRecentMissionsList error: ${JSON.stringify(err)}`);
        });
      if (CheckEmptyUtils.isEmptyArr(listData)) {
        Log.showInfo(TAG, 'getRecentMissionsList Empty');
        return recentMissionsList;
      }
      for (const recentItem of listData) {
        const recentMissionInfo = new RecentMissionInfo();
        recentMissionInfo.missionId = recentItem.missionId;
        recentMissionInfo.bundleName = recentItem.want.bundleName;
        recentMissionInfo.abilityName = recentItem.want.abilityName;
        recentMissionInfo.lockedState = recentItem.lockedState;
        const appInfo = await launcherAbilityManager.getAppInfoByBundleName(recentMissionInfo.bundleName, recentMissionInfo.abilityName);
        if (appInfo == undefined) {
          continue;
        }
        recentMissionInfo.appLabelId = appInfo.appLabelId;
        recentMissionInfo.appIconId = appInfo.appIconId;
        recentMissionInfo.appName = appInfo.appName;
        recentMissionsList.push(recentMissionInfo);
      }
      Log.showInfo(TAG, `getRecentMissionsList recentMissionsList length: ${recentMissionsList.length}`);
      return recentMissionsList;
    }
  • 通过日志输出信息确认:的确是获取的recentMissionInfo信息有误,例:

    08-08 10:08:38.403 1550 1550 I 02200/JsApp: Launcher_Default tag: AmsMissionManager --> getRecentMissionsList res: [{"missionId":8,"runningState":-1,"lockedState":false,"continuable":false,"timestamp":"1502186916","want":{"deviceId":"","bundleName":"com.uniontech.fileManager","abilityName":"com.uniontech.entry.MainAbility","uri":"","type":"","flags":0,"action":"","parameters":{"ohos.aafwk.param.callerPid":1550,"ohos.aafwk.param.callerToken":537040792,"ohos.aafwk.param.callerUid":20010018},"entities":[]},"label":"$string:entry_MainAbility","iconPath":"$media:icon"},{"missionId":9,"runningState":0,"lockedState":false,"continuable":false,"timestamp":"1502186916","want":{"deviceId":"","bundleName":"com.ohos.settings","abilityName":"com.ohos.settings.MainAbility","uri":"","type":"","flags":0,"action":"","parameters":{"ohos.aafwk.param.callerPid":1550,"ohos.aafwk.param.callerToken":537040792,"ohos.aafwk.param.callerUid":20010018},"entities":[]},"label":"$string:entry_MainAbility","iconPath":"$media:icon"},{"missionId":7,"runningState":-1,"lockedState":false...
  • 通过日志分析:MissionsList中顺序有误的应用fileManager和settings的timestamp时间是一样的。

  • 继续从源码层面排查,排查MissionsList中信息的插入和更新,发现:mission的该属性值只有在应用拉起的时候和最小化的时候进行插入和更新。

  • 时间字段记录精度为秒:

    info.missionInfo.time = Time2str(time(0));
  • MissionInfo插入逻辑:通过时间判断,当当前记录的时间小于列表中的记录的时间时,则插入当前位置,可保证顺序为最近操作顺序

    bool MissionInfoMgr::AddMissionInfo(const InnerMissionInfo &missionInfo)
    {
        auto id = missionInfo.missionInfo.id;
        if (missionIdMap_.find(id) != missionIdMap_.end() && missionIdMap_[id]) {
            HILOG_ERROR("add mission info failed, missionId %{public}d already exists", id);
            return false;
        }
    ​
        auto listIter = missionInfoList_.begin();
        for (; listIter != missionInfoList_.end(); listIter++) {
            if (listIter->missionInfo.time < missionInfo.missionInfo.time) {
                break;  // first listIter->time < missionInfo.time
            }
        }
    ​
        if (!taskDataPersistenceMgr_->SaveMissionInfo(missionInfo)) {
            HILOG_ERROR("save mission info failed");
            return false;
        }
    ​
        missionInfoList_.insert(listIter, missionInfo);
        missionIdMap_[id] = true;
        return true;
    }
  • 进一步分析,发现:当时间相同时,插入会有问题,因为不满足时间小于的逻辑,会导致插入顺序有误,从而无法获取正确的任务列表。

  • 继续分析:上一个应用最小化时会更新其应用时间,然后迅速打开另一个应用,当两个应用的时间为同一秒时,那么新拉起的应用插入的MissionInfoList顺序将出现问题。

 

6 知识分享

  • C++中的时间函数time(0)精度只到秒,若想保证时间的绝对有序性,需考虑记录时间毫秒数。

Logo

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

更多推荐