最后再说一次!!不要在你的 App 启动界面设置 SingleTask-SingleInstance
···//不为空时进入该循环
if (reusedActivity != null) {// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but// still needs to be a lock task mode violation since the task gets cleared out and// the device would otherwise leave the locked task.······
// This code path leads to delivering a new intent, we want to make sure we schedule it// as the first operation, in case the activity will be resumed as a result of later// operations.//isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)表示启动模式为或者 SingleInstance 或者 SingleTask 时,进入该判断
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0|| isDocumentLaunchesIntoExisting(mLaunchFlags)|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {final TaskRecord task = reusedActivity.getTaskRecord();
// In this situation we want to remove all activities from the task up to the one// being started. In most cases this means we are resetting the task to its initial// state.//大多数情况下我们可能准备清空当前 task 或者回到 task 的初始状态 final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,mLaunchFlags);
// The above code can remove {@code reusedActivity} from the task, leading to the// the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The// task reference is needed in the call below to// {@link setTargetStackAndMoveToFrontIfNeeded}.if (reusedActivity.getTaskRecord() == null) {reusedActivity.setTask(task);}
if (top != null) {//是否为根 activity
//boolean frontOfTask; // is this the root activity of its task?if (top.frontOfTask) {// Activity aliases may mean we use different intents for the top activity,// so make sure the task now has the identity of the new intent. //设置启动 Activity 为根 Activitytop.getTaskRecord().setIntent(mStartActivity);}//将会调用该 Activity 的 onNewIntent,一旦调用了 mStartActivity,因为我们也设置了 SingleTask 或者 SingleInstance,所以我们每次看到的都是 mStartActivitydeliverNewIntent(top);}}}······}
先来看看 getReusableIntentActivity 方法,看看该方法的注释,很快就明白作用了,所以返回的不是 null,所以会进入上面的判断逻辑中
/**
Decide whether the new activity should be inserted into an existing task. Returns null
if not or an ActivityRecord with the task into which the new activity should be added.*/private ActivityRecord getReusableIntentActivity() {// We may want to try to place the new activity in to an existing task. We always// do this if the target activity is singleTask or singleInstance; we will also do// this if NEW_TASK has been requested, and there is not an additional qualifier telling// us to still place it in a new task: multi task, always doc mode, or being asked to// launch this as a new task behind the current one.
再来看看 deliverNewIntent 中被调用到的 deliverNewIntentLocked,最终决定哪个 Activity 的 onNewIntent 会被调用到,也就是我们的 mStartActivity
/**
Deliver a new Intent to an existing activity, so that its onNewIntent()
method will be called at the proper time.*/final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {// The activity now gets access to the data associated with this Intent.
首次安装可能带来的问题
在开发过程中,安装完成一个 app 时,在安装界面直接点击打开。我们进入了 app 的首页,这时我们按 home 键返回桌面,再点击应用图标,会发现没有直接进入首页,而是先进入了 app 的闪屏页,在进入首页。重复这一步一直如此。这时我们按 back 键返回,发现没有直接退回桌面,而是返回到之前打开的多个首页。但是如果一开始安装完我们不是直接打开,而是在桌面点击应用进入就不会这样了。
解决方案
在你
的闪屏界面,或者没有闪屏界面,像我上面启动界面直接就是 MainActivity 的话,那你就在该界面的 onCreate 方法中直接添加下面这段代码。具体的分析可以见下面这篇文章
if (!this.isTaskRoot()) { // 当前类不是该 Task 的根部,那么之前启动 Intent intent = getIntent();if (intent != null) {String action = intent.getAction();if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(action)) { // 当前类是从桌面启动的 finish(); // finish 掉该类,直接打开该 Task 中现存的 Activityreturn;}}}
总结
千万要注意,不要在你的启动界面(如果你想把 MainActivity 的 windowbackground 设置为闪屏界面,移除闪屏页,直接启动 MainActivity 给用户造成快速启动的感觉)设置启动模式为 SingleTask 或者 SingleInstance,一旦设置后,不管软启动或者热启动都是从该启动界面开始启动 App,除非特殊的需求,否则千万不要这么设置。如果想要实现类似 SingleTask 的清栈效果,可以使用 standard 或者 singleTop 结合对应的 Flag 进行实现。
最后说一下另一种情况,如果你一定想要在启动页设置为 SingleTask/SingleInstance(不设置活不下去了),那也有办法,就是添加个闪屏界面(SplashActivity 设置为 SingleTask/SingleInstance),然后启动 MainActivity,这里千万要注意,闪屏界面(SplashActivity)一定要及时关掉,,同时在闪屏页面的 onCreate 方法中添加如下代码,否则你每次点击 app 图标都是从闪屏页开始显示了。但是这样上面的移除闪屏页快速启动的优化就没意义了。
@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);Log.i(TAG, "onCreate: =========");//关键代码 if (!isTaskRoot()) {Intent intent = getIntent();if (intent != null) {if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(intent.getAction())) {finish();return;}}}Intent intent = new Intent(this, MainActivity.class);startActivity(intent);finish();}
笔记
【360°全方位性能调优】
这份笔记我将 Android-360°全方位性能优化知识点,以及微信、淘宝、抖音、头条、高德地图、优酷等等亿万级用户 APP 在性能优化方面的实践经验,整合成了一套系统的知识笔记 PDF,从理论到实践,涉及 Android 性能优化的所有知识点,长达 721 页电子书!相信看完这份文档,你会对 Android 性能调优知识体系及各种方案有更系统、更深入的理解。
需要的小伙伴点赞+关注后在我的GitHub即可直接免费下载获取~
参考文章
评论