Activity发动形式与栈的运用小结

前言

最近碰到了挺多和activity栈相关的内容,想来略微总结下,前段时间写了篇taskAffinity相关的博客,可是感觉写的不咋样,这次就把脑子里相关的内容收拾下吧。

四种发动形式

Activity的四种发动形式,这个估计是老生常谈的问题了,这儿就提下留意点,首要便是合作FLAG的问题。

Standard

默许的发动方法,栈顶新建嘛,每次都会发动一个新的activity实例。

需求留意的便是合作FLAG运用的时分:

  1. FLAG_ACTIVITY_NEW_TASK
    合作FLAG_ACTIVITY_NEW_TASK一同运用时,设置了taskAffinity会新开或者复用一个栈(对应taskAffinity),假如都是默许taskAffinity那就无效了。

  2. FLAG_ACTIVITY_CLEAR_TOP
    合作FLAG_ACTIVITY_CLEAR_TOP一同运用时,会将可复用的activity上面以及该activity都清除了,再创立个新的activity。

    假如再加上FLAG_ACTIVITY_SINGLE_TOP时,该可复用的activity就不会被清楚,而是复用,走onNewIntent方法。

  3. FLAG_ACTIVITY_SINGLE_TOP
    这儿便是和singleTop相同的效果了。

SingleTop

栈顶复用,复用的时分会走onNewIntent,没复用就在当时栈新建,等同于FLAG_ACTIVITY_SINGLE_TOP。

SingleTask

这种形式发动的Activity只会存在相应的Activity的taskAffinity使命栈中,同一时间系统中只会存在一个实例,已存在的实例被再次发动时,会从头引发该实例,并清理当时Task使命栈该实例之上的一切Activity,同时回调onNewIntent()方法。

假如指定的task不存在,创立指定的taskAffinity的task,也便是说并不一定在当时栈的栈顶创立一个新的activity,下面看下几种FLAG的组合,就会了解深一点:

  1. FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP
    在当时栈中复用activity,并将该activity上面的activity清出栈,调用onNewIntent方法。假如没有复用,仍是在当时栈新建一个activity。

  2. FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK
    FLAG_ACTIVITY_NEW_TASK一般就和FLAG_ACTIVITY_CLEAR_TASK、FLAG_ACTIVITY_CLEAR_TOP一同运用。

    这儿假如有对应taskAffinity的task,就把栈清空,并创立一个activity作为唯一成员。

    假如没有对应taskAffinity的task,会新建一个对应taskAffinity的task,新建activity并放入。

  3. FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP
    假如有对应taskAffinity的task,且有可复用的activity,就把它以及它以上的activity清空,创立个新的activity放在栈顶,没可复用就栈顶新建。

    假如没有对应taskAffinity的task,会新建一个对应taskAffinity的task,新建activity并放入。

看完上面这几个例子能了解SingleTask对应什么FLAG了吗?在我看来,上面四个都不对,更应该是这三个的合成:

FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP

( 可是这儿会有问题,FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP放一同的时分,会先验证FLAG_ACTIVITY_SINGLE_TOP,假如当时栈正好是可复用的activity,就不会验证task问题,直接复用了。假如栈顶没有的可复用的activity的话,会在对应taskAffinity的task栈中找,z找到了再清空该activity上层,留意这儿会复用!)

ps. 这儿我后面又写了个DEMO试了下,从其他博客看的如同不太对,能够看下这篇文章:

对 FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP 的实践

可是假如真看一般运用的话,FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP应该更符合咱们的需求。

SingleInstance

这种形式发动的Activity独自占用一个Task使命栈,同一时间系统中只会存在一个实例,已存在的实例被再次发动时,只会引发原实例,并回调onNewIntent()方法。

这儿不用想对应的FLAG了,由于它没有对应的FLAG。

Intent的FLAG分析

在上面的内容中,咱们看到了,四种发动形式看起来简单,可是加上FLAG之后就变得特别费事了,关于FLAG我也没什么好说的了,之前转载了一篇博客,还有一些资料能够提供给读者看下,写的非常好:

Android Intent的FLAG标志详解

Android面试官装逼失败之:Activity的发动形式

官方文档

taskAffinity及allowTaskReparenting

taskAffinity,能够翻译为使命相关性。这个参数标识了一个 Activity 所需求的使命栈的姓名,默许状况下,一切 Activity 所需的使命栈的姓名为运用的包名,当 Activity 设置了 taskAffinity 特点,那么这个 Activity 在被创立时就会运行在和 taskAffinity 姓名相同的使命栈中,假如没有,则新建 taskAffinity 指定的使命栈,并将 Activity 放入该栈中。另外,taskAffinity 特点首要和 singleTask 或者 allowTaskReparenting 特点配对运用,在其他状况下没有意义(不收效)。

allowTaskReparenting,翻译过来的意思是答应从头找爸爸妈妈(答应搬迁使命栈),该特点用于装备是否答应该activity替换从属的task。
假如一个Activity设置了这个特点,其他运用发动这个activity的时分分两种状况处理:

  • 这个activity对应的进程现已发动了:则这个activity直接隶属到自己所对应的进程的运用栈上
  • 这个activity对应的进程没有发动:则这个activity先直接隶属到发动它的运用的运用栈上,当activity对应的进程发动后,则会自动搬迁到activity对应的进程。

官方解说:

当下一次将发动 Activity 的使命转至前台时,Activity 是否能从该使命转移至与其有相似性的使命 -“true”表明能够转移,“false”表明仍须留在发动它的使命处。

风趣说法:

你捡到一条狗,在家里喂食几天觉得不错,当自己家的了;可是突然有一天他的主人找上门来了,小狗仍是乖乖和主人走了。这条狗便是带有allowTaskReparenting特点的activity。

其他问题

在Service内发动Activity

这个是面试的时分被问到的,在Service内发动Activity,会报错,需求增加FLAG_ACTIVITY_NEW_TASK,当时我觉得报错闪退是由于两个不在同一个栈内,现在看来仍是太天真了。

这儿其实是安卓设置成这样的,能够看下下面这篇文章,我觉得写道挺好的:

Android 在Service中发动Activity的大坑

最近使命中多个页面

假如有多个使命栈,在手机运用卡片里边可能会有多个页面,这儿和咱们的taskAffinity有关。

发生原因

假如SingleTask的activity(或者FLAG_ACTIVITY_NEW_TASK发动),其taskAffinity不相同,那就会产生多个使命卡片。

前面处理StrandHogg缝隙的时分,我把application的taskAffinity设置为空字符串也会触发这种状况,默许的taskAffinity是运用的包名。

处理方法

这儿有好几种方法:

  1. 经过设置manifest的android:excludeFromRecents=”true”处理
  2. 经过FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS处理
  3. 经过对ActivityManager.AppTask设置setExcludeFromRecents处理,复杂些。
  4. 经过对activity设置非SingleTask形式,并经过FLAG发动来处理。

交流Activity的次序

在我遇到的状况便是,希望两个页面互切不finish,大致便是这样一个状况:A -> B, B -> A, A – > B,希望B或A只是交流。

这儿一开端我觉得会很费事,结果便是加一个FLAG_ACTIVITY_REORDER_TO_FRONT就行了,实际便是对Activity的交流,并且这儿会触发onNewIntent方法,很实用。

获取栈信息

有时分想要获取Activity的信息,打log就有点费事了,能够用adb命令:

adb shell
dumpsys activity top | grep ACTIVITY

在代码中就能够用下面代码:

// 只能获取当时栈顶和设置的baseActivity(stack最开端activity)
ActivityManager am = (ActivityManager) RunningTaskInfo context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTaskInfoList = am.getRunningTasks(1);
ActivityManager.RunningTaskInfo info = runningTaskInfoList.get(0);
info.baseActivity;
info.topActivity;

这儿的topActivity便是顶层activity,baseActivity是stack最开端activity,相应的还有个rootActivity的概念,也是这样,能够经过Activity的isTaskRoot()判别。