Android Tasks、ActivityStack和Activity

最近又在开展一些Android的开发工作,出现了在个别场景下发生的Activity切换不符合预期的情况,于是来温习一下Android的Tasks、ActivityStack、Activity,以及影响到他们的几个重要参数launchMode、taskAffinity、IntentFlags

应用程序的进程与线程

从应用程序的角度来看,应用程序一般定义了应用对应的进程,以及上面运行着的一系列的线程。开发人员进行debug的时候的关注点会是哪个进程的哪个线程的哪个stacktrace。

Android四大组件

从Android应用程序的角度来看,除了进程和线程的这一个角度,从程序里面详细类的组织来看,Android应用程序包含有Activity、Service、ContentProvider、Broadcast Receiver4大组件,这四大组件就是通过我们之前说过的XML插件方式配置到AndroidManifest当中的。而这其中,Activity组件是Android应用程序界面开发最重要的一个部分。

Android系统界面用户体验组织(TASKS)

从Android系统界面的组织来看,用户体验到的直观界面都是由很多Tasks组成的。比如,我们从最近任务栏,就可以看到很多TASKS。用户可以随意的切换到其中的一个TASK。也可以按HOME键退出一个TASK。TASK的组织形式是ActivityStack,一个ActivityStack是由好多Activity组成的堆栈。从Android系统设计者的角度来看,一个Task定义了一组行为,而这组行为是由跨多个应用程序的多个Activity组织而构成的。这个打破了以往由应用程序(进程)来定义的资源边界。一个Task就是一个场景的实现。从此构建系统用户体验和行为的边界在于TASK,而非应用程序或者进程。

Task

  • Task 是activities的集合,通过activity stack来管理,依靠先进后出队列来实现;
  • 每个task中都至少有一个activity,新实例出来的activity置于栈顶
  • Task可以被切换到后台

Activity Stack  

  • 如上所诉,Activity承担了大量的显示和交互工作,从某种角度上将,我们看见的应用程序就是许多个Activity的组合。为了让这许多 Activity协同工作而不至于产生混乱,Android平台设计了一种堆栈机制用于管理Activity,其遵循先进后出的原则,系统总是显示位于栈 顶的Activity,从逻辑上将,位于栈顶的Activity也就是最后打开的Activity,这也是符合逻辑的。
  • 在操作应用程序时,每次启动新的Activity,都会将此压入Activity Stack,当用户执行返回操作时,移除Activity Stack顶上的Activity,这样就实现了返回上一个Activty的功能。直到用户一直返回到Home Screen,这时候可以理解为移除了Activity Stack所有的Activity,这个Activity Stack不再存在,应用程序也结束了运行。
  • 可以通过 adb shell dumpsys |grep ActivityRecord 来查看 TASKS的ActivityStacks
  • 可以通过 adb shell dumpsys activity activities |grep packageName| grep Run 来查看某个packageName的ActivityStatcks

 

task的taskAffinity

  • taskAffinity 这个属性主要是决定持有每个activity属于哪个task。
  • 默认情况下,同一个包中的activity共享同一个affinity(任务共用性)。

 

task的launchMode

  • standard(default):standard,标准的Activity是可以随意插入到TASK中去的一个组织结构,可以去TaskA,也可以去TaskB,也可以去TaskC,直接并无任何的联系
  • singleTop,如果在任务的栈顶正好存在该Activity的实例,就重用该实例,否则就创建新的实例并放入栈顶。
  • singleTask,如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移除栈。如果栈中不存在该实例,将会创建新的实例放入栈中。
  • singleInstance

Intent Flag介绍

  • FLAG_ACTIVITY_CLEAR_TOP    

      如果设置,并且这个Activity已经在当前的Task中运行,因此,不再是重新启动一个这个Activity的实例,而是在这个Activity上方的所有Activity都将关闭,然后这个Intent会作为一个新的Intent投递到老的Activity(现在位于顶端)中。      例如,假设一个Task中包含这些Activity:A,B,C,D。如果D调用了startActivity(),并且包含一个指向Activity B的Intent,那么,C和D都将结束,然后B接收到这个Intent,因此,目前stack的状况是:A,B。      上例中正在运行的Activity B既可以在onNewIntent()中接收到这个新的Intent,也可以把自己关闭然后重新启动来接收这个Intent。如果它的启动模式声明为“multiple”(默认值),并且你没有在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,那么它将关闭然后重新创建;对于其它的启动模式,或者在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,都将把这个Intent投递到当前这个实例的onNewIntent()中。      这个启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合起来使用:用于启动一个Task中的根Activity,它会把那个Task中任何运行的实例带入前台,然后清除它直到根Activity。这非常有用,例如,当从Notification Manager处启动一个Activity

  • FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET    

      如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。      这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。

     
  • FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

      If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed.

     
  • FLAG_ACTIVITY_NEW_TASK     

       如果设置,这个Activity会成为历史stack中一个新Task的开始。一个Task(从启动它的Activity到下一个Task中的Activity)定义了用户可以迁移的Activity原子组。Task可以移动到前台和后台;在某个特定Task中的所有Activity总是保持相同的次序。      这个标志一般用于呈现“启动”类型的行为:它们提供用户一系列可以单独完成的事情,与启动它们的Activity完全无关。      使用这个标志,如果正在启动的Activity的Task已经在运行的话,那么,新的Activity将不会启动;代替的,当前Task会简单的移入前台。参考FLAG_ACTIVITY_MULTIPLE_TASK标志,可以禁用这一行为。      这个标志不能用于调用方对已经启动的Activity请求结果。

     
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS    

      如果设置,新的Activity不会在最近启动的Activity的列表中保存。

  • 参考一个stackoverflow的问答 https://stackoverflow.com/questions/7759556/flag-activity-exclude-from-recents-excludes-whole-application-not-only-the-acti

  • I have a Notification which starts an Activity. After a long press on home button and selecting my app, I want to start my main Activity again, and not this Activity started by the Notification. I tried with FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, but this removed my whole application from the recents, and that's not what I want to achieve. How can I have my app in the recents, but have the main Activity started?

  • Okay, I found the solution to my problem. I started an Activity from a Notification with FLAG_ACTIVITY_NEW_TASK. But it seems to me that this Activity only gets started in an own task if affinity is different from the default affinity. So I had to add a different affinity in the manifest.

    And it seems that FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS does not (as documented) exlucde the Activity from the recents, rather it excludes the whole task (not the whole application) in which the Activity gets started from the recents. And as I hadn't set a different affinity the Activity which I wanted to exclude was started in the same task (although I had set FLAG_ACTIVITY_NEW_TASK) and so my whole application (as it was running in only one task) was excluded from the recents.

    Now I've set a different affinity for the Activity that gets started from the Notification and I start it with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. When I leave this Activity and long-press the HOME button I can choose my app and the default task is started or brought to the front.

  • FLAG_ACTIVITY_FORWARD_RESULT     

     

      如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的Activity。

猜你喜欢

转载自blog.csdn.net/vshuang/article/details/66472338