工作中遇到这么个问题,com.kos.launcher是自写app,运行该app,并切到后台,然后点击桌面com.kos.launcher图标。出现行为不是com.kos.launcher被切到前台,而是重新启动了com.kos.launcher这个app。
一、app层:修改onCreate
“处理app在后台运行,点击桌面图标重新启动问题”提供种解决办法:判断启动页是否是根任务,如果不是,则不初始化启动页。在启动页的onCreate添加下面代码。
@Override protected void onCreate(Bundle savedInstanceState) { if (!isTaskRoot()&& getIntent().getAction() != null) { finish() return } super.onCreate(savedInstanceState) }
经过这么修改后,等android启动完成,再手动运行com.kos.launcher,似乎是得到了期望结果。也说就是,再点击图标,是把com.kos.launcher切到前台。但是,如果com.kos.launcher是随机启动,那第一次单击图标时,还是会杀死原来那个com.kos.launcher。
要求com.kos.launcher必须随机启动,只好试着修改android源码。
二、android源码:修改Task.isSameIntentFilter
系统维护着一个当前正运行哪些app的列表,当点击com.kos.launcher图标,如果从列表能找到对应app,会把com.kos.launcher切到前台。否则要运行一个新的com.kos.launcher,由于列表中有一个旧的com.kos.launcher,于是看到行为是旧的com.kos.launcher被杀死,新的被运行。
不论旧的,还是新的,bundleid都是com.kos.launcher,但android在判断列表中是否存在正单击桌面图标的app时,应该不止bundleid必须相同,还有其它条件。查下来问题出在Task.isSameIntentFilter。
<aosp>/frameworks/base/services/core/java/com/android/server/wm/Task.java ------ class Task extends TaskFragment { boolean isSameIntentFilter(ActivityRecord r) { final Intent intent = new Intent(r.intent); // Make sure the component are the same if the input activity has the same real activity // as the one in the task because either one of them could be the alias activity. if (Objects.equals(realActivity, r.mActivityComponent) && this.intent != null) { intent.setComponent(this.intent.getComponent()); } if (intent.getComponent() != null && intent.getComponent().getPackageName() != null) { // 这个if块是我加的,com.kos.launcher时,强制认为一样。 String rPackageName = r.mActivityComponent.getPackageName(); if (intent.getComponent().getPackageName().equals(rPackageName) && rPackageName.equals("com.kos.launcher")) { Slog.d(TAG, "{leagor}isSameIntentFilter(1), it is " + rPackageName + " always return true"); Slog.d(TAG, "{leagor}isSameIntentFilter(2), intent: " + intent + " this.intent: " + this.intent); return true; } } return intent.filterEquals(this.intent); } }
解决办法是修改isSameIntentFilter,增加那第二个if。让bundleid是com.kos.launcher,总是返回一致。以下是出问题的两个intent。
intent: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10600000 cmp=com.kos.launcher/.app bnds=[960,194][1275,335] } this.intent: Intent { flg=0x10000000 cmp=com.kos.launcher/.app }
用了这种方法后,没必要用上面写的修改onCreate了。
三、何时调用Task.isSameIntentFilter
为查看更多android运行时日志,建议打开几个输出。
<aosp>/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java ------ static final boolean DEBUG_ALL = true; <aosp>/frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java ------ private static final boolean DEBUG_RESOLVER = true; 如果想再看更低层的performLaunchActivity、handleLaunchActivity。修改这个,编译时间会较长。 <aosp>/frameworks/base/core/java/android/app/ActivityThread.java ------ static final boolean localLOGV = true;
网上有文章描述Android Activity启动过程,像“Android Activity——启动过程探索(二)”。
class ActivityStarter; class Task extends TaskFragment; class TaskFragment extends WindowContainer<WindowContainer>; ActivityStarter.executeRequest ActivityStarter.startActivityUnchecked ActivityStarter.startActivityInner ActivityStarter.recycleTask ActivityStarter.setTargetRootTaskIfNeeded。 ActivityStarter.complyActivityFlags
ActivityStarter.complyActivityFlags会调用Task.isSameIntentFilter。以下是通过看两处logcat定位问题。
第一处:“Prepare open transition”、“Resuming”
出错时(点击图标重新运行了app)
V/ActivityTaskManager: moveTaskToFront: Task{282c450 #13 type=standard A=10078:com.kos.launcher U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1} V/ActivityTaskManager: Prepare to front transition: task=Task{282c450 #13 type=standard A=10078:com.kos.launcher U=0 visible=true visibleRequested=false mode=fullscreen translucent=true sz=1} I/ActivityTaskManager: Update options for ActivityRecord{7fbb24d u0 com.kos.launcher/.app t13} V/ActivityTaskManager: Prepare open transition: starting ActivityRecord{b2a2e8c u0 com.kos.launcher/.app t13}
正确时(点击图标把app切到前台)
V/ActivityTaskManager: moveTaskToFront: Task{ae03f6f #14 type=standard A=10078:com.kos.launcher U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1} V/ActivityTaskManager: Prepare to front transition: task=Task{ae03f6f #14 type=standard A=10078:com.kos.launcher U=0 visible=true visibleRequested=false mode=fullscreen translucent=true sz=1} I/ActivityTaskManager: Update options for ActivityRecord{a62924e u0 com.kos.launcher/.app t14} V/ActivityTaskManager: Resuming ActivityRecord{a62924e u0 com.kos.launcher/.app t14}
第四条由“Prepare open transition ...”变成了“Resuming ActivityRecord...”。
第二处:TransactionExecutor.execute的ClientTransaction参数
过程中会调用TransactionExecutor.execute。
<aosp>/frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java ------ public void execute(ClientTransaction transaction)
DEBUG_RESOLVER==true时,会用transactionToString(transaction, mTransactionHandler)向logcat输出参数ClientTransaction信息。
出错时(点击图标重新运行了app)
1888-1888/com.kos.launcher D/TransactionExecutor: tId:-1211256274 Start resolving transaction 1888-1888/com.kos.launcher D/TransactionExecutor: tId:-1211256274 ClientTransaction{ tId:-1211256274 callbacks=[ tId:-1211256274 LaunchActivityItem{intent=Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10600000 cmp=com.kos.launcher/.app bnds=[960,194][1275,335] },ident=187313804,info=ActivityInfo{e0126d com.kos.launcher.app},curConfig={1.0 ?mcc?mnc [en_US] ldltr sw617dp w1097dp h545dp 280dpi lrg long land -touch -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1920, 1080) mAppBounds=Rect(0, 0 - 1920, 996) mMaxBounds=Rect(0, 0 - 1920, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} as.2 s.8 fontWeightAdjustment=0},overrideConfig={1.0 ?mcc?mnc [en_US] ldltr sw617dp w1097dp h545dp 280dpi lrg long land -touch -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1920, 1080) mAppBounds=Rect(0, 0 - 1920, 996) mMaxBounds=Rect(0, 0 - 1920, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} as.2 s.1 fontWeightAdjustment=0},referrer=null,procState=10,state=null,persistentState=null,pendingResults=null,pendingNewIntents=null,options=null,profilerInfo=null,assistToken=android.os.BinderProxy@6d6a897,rotationAdj=null,shareableActivityToken=android.os.BinderProxy@c8dfa84} tId:-1211256274 ] tId:-1211256274 stateRequest=ResumeActivityItem{procState=-1,updateProcState=false,isForward=true} tId:-1211256274 } tId:-1211256274 Target activity: Not found for token: android.os.BinderProxy@309c716
android没有找到该app,于是LaunchActivityItem,重新Launche一个。
正确时(点击图标把app切到前台)
2134-2134/com.kos.launcher D/TransactionExecutor: tId:899264136 Start resolving transaction 2134-2134/com.kos.launcher D/TransactionExecutor: tId:899264136 ClientTransaction{ tId:899264136 callbacks=[ tId:899264136 TopResumedActivityChangeItem{onTop=true} tId:899264136 ] tId:899264136 stateRequest=null tId:899264136 } tId:899264136 Target activity: com.kos.launcher.app
android找到了该app,于是TopResumedActivityChangeItem,把该app切到前台。