Launcher获取recentView时顺序错误问题分析报告
1 关键字
Launcher;RecentView;missionInfo;mission_list_manager
2 问题描述
问题现象:Launcher获取recentView时顺序错误,不是应用打开顺序,出现几率30%。
运行环境:硬件 dayu200,软件:3.1release
测试步骤:
-
打开browser,看到界面后,点击“Home”或者“Back”键。
-
打开fileManager,看到界面后,点击“Home”或者“Back”键。
-
打开settings,看到界面后,点击“RecentView“,进入RecentView窗口。
-
依次循环操作,有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)精度只到秒,若想保证时间的绝对有序性,需考虑记录时间毫秒数。
更多推荐


所有评论(0)