写点什么

最后再说一次!!不要在你的 App 启动界面设置 SingleTask-SingleInstance

用户头像
Android架构
关注
发布于: 1 小时前

···//不为空时进入该循环


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 键返回,发现没有直接退回桌面,而是返回到之前打开的多个首页。但是如果一开始安装完我们不是直接打开,而是在桌面点击应用进入就不会这样了。

解决方案

在你


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


的闪屏界面,或者没有闪屏界面,像我上面启动界面直接就是 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即可直接免费下载获取~



参考文章

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
最后再说一次!!不要在你的App启动界面设置SingleTask-SingleInstance