您的位置:首页 > 移动开发 > Android开发

理解Android的Activity launchMode:standard,singleTop,singleTask,singleInstance

2017-02-11 16:40 489 查看
[html] view
plain copy

  

[html] view
plain copy

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">原文地址:</span><a target=_blank href="http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en" target="_blank" style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en</a>  

       Activity是Android在内存管理的优秀架构中最好的概念之一,它使得多任务能够在Android这个著名的移动操作系统完美地运行。
       不管怎样,Activity不仅仅是被加载到屏幕上的(这句翻译怪怪的!),它的加载方式也需要我们关注。关于Activity加载这个话题,有着很多的细节。这些细节中真正重要的一个就是launchMode,而它也是我们将要在本文中讨论的。
            因为每个Activity都有着不同的使用目的。有的Activity是设计用来独立处理每个一个发送来的Intent,例如在email客户端中撰写邮件的Activity,而有的则是以“单例”的模式工作,例如一个email的收件箱Activity。
       这就是为什么在指定一个Activity时,到底是需要创建一个新的还是使用已有的这个问题是比较重要的了,这个问题会也许导致不好的用户体验。感谢Android的核心工程师,他们针对这个问题特别提供了一个launchMode属性来容易地解决。

指定launchMode

          最基本的,我们可以直接在AndroidManifest.xml中的<activity>标签中作为一个属性来指定launchMode:

[html] view
plain copy

<activity  

android:name=".SingleTaskActivity"  

android:label="SingleTask launchMode"  

android:launchMode="singleTask"/>  

一共有4种可以用的launchMode,我们将一个接一个的研究。

standard

      这是一个默认模式。
      设定为这个模式的Activity在收到Intent时总是创建一个新的Activity。想象一下,如果有10个Intent要撰写email,那么就会有10个Activity加载出来响应每一个Intent。这样就会导致在一个设备上加载的这种Activity的数量不会有限制。

      Lollipop(Android 5.0)之前的Android版本表现形式

      这种Activity将会被创建并放在发送Intent的Activity所在任务栈的顶端(如下图所示)。



       下面的图片展示了但我们向一个standard模式的Activity分享图片时会发生什么。跟之前描述的一样,这个Activity将会被放在相同任务栈中,尽管这两个Activity来自不同的应用。



       在任务管理器中,你将会看到如下界面。



      如果我们从该应用切换到另一个,然后再切回Gallery,我们将会看到standard加载模式的Activity是放在Gallery的任务栈顶端的。这导致了如果我们想在Gallery中做些什么就必须先结束这个新增Activity中的工作。

Android Lollipop(Android 5.0)中的表现形式:

      如果这些Activity是在同一个应用中的,那么它的加载方式跟Lollipop之前版本中形式一样,会把新的Activity放在发送Intent的Activity所在任务栈的顶端。



      但是当Intent是来自另一个不同的应用时,就会创建一个新的任务,然后这个新建的Activity会以一个根Activity的形式放在新建的任务中。



      在你的任务管理器中你将会看到如下的界面:



      这种改变发生的原因是Lollipop中的任务管理器系统进行了修改,使得表现地更好、更有道理。在Lollipop中,你可以仅仅切换回Gallery,因为它们在不同的任务栈中。你可以启动另一个Intent,然后就像之前那样,一个新的任务将会被创建来响应该Intent。



      这种类型Activity的的一个例子就是撰写email的Activity或者一个社交网络应用中发帖的Activity。如果你想让Activity独立的响应不同的intent,那就考虑使用standard launchMode。

singleTop

      singleTop模式跟standard模式很像,因为singleTop模式的Activity实例也可以创建很多。唯一不同的就是如果已经存在一个相同类型的Activity实例在Intent发送者所在任务栈的顶端,就不会再创建新的Activity,相反,这个Intent将会通过onNewIntent()方法发送给已经存在的Activity。



      在singleTop模式中,你必须在onCreate()和onNewIntent()方法中处理一个进来的Intent,以确保在任何情况下都可以有效工作。
      一个使用这种加载模式的例子就是“查找功能”。考虑一下创建一个将使你进入SearchActivity查看搜索结果的搜索框,为了更好的用户体验,一般我们总是将搜索框放在搜索结果页面中以便用户不需要按返回键就可以直接搜索。
      现在想象一下,如果我们总是加载一个新的SearchActivity来响应新的搜索结果,如果搜索10次将会有10个SearchActivity。当你按返回键返回时,你必须按10次才可以穿过这些搜索结果Activity,然后返回到你的根Activity。
      如果有一个SearchActivity在任务栈的顶端,我们最好是向存在的Activity实例发送Intent,然后让它更新搜索结果。现在就只有一个SearchActivity在任务栈的顶端,你可以简单按一次返回键就可以返回到以前的Activity。
      不管怎样,singleTop会和调用者工作在相同的任务栈中。如果你希望发送一个Intent到放在另一栈顶端已经存在的Activity,我不得不遗憾的告诉你这是不行的。如果Intent是从另一个应用发送到singleTop Activity的,一个新的Activity会以standard加载模式加载出来(Lollipop之前版本:新Activity放在调用者栈顶端,Lollipop:创建一个新的任务栈)。

singleTask

      这个模式与standard和singleTop有着很大不同。singleTask加载模式的Activity在系统中只允许有一个实例(也叫单例)。如果系统中已经存在一个Activity实例,持有实例的任务将会被移动到顶端,同时Intent将通过onNewIntent()方法传递。否则,将会创建一个新的Activity然后放在合适的任务栈中。

      在相同的应用中

      如果系统中不存在一个singleTask的Activity,系统会创建一个新的Activity然后让在相同任务栈顶端(如下图所示)。



      但是如果系统中已经存在,任务栈中所有放在singleTask Activity上面的Activity将会被自动并且残酷的以正常方式销毁掉(触发生命周期),从而使得我们想要的Activity出现在栈的顶端。同时,Intent将通过可爱的onNewIntent()方法发送到singleTask Activity。



      从用户体验角度来讲,这是不好的,但是它就是这样设计的。。。
      你需要注意文档中提到的一件事:

[html] view
plain copy

The system creates a new task and instantiates the activity at the root of the new task.  

      但是从实验老看,它好像不是像文档中描述那样工作的。singleTask Activity仍然会堆积在任务栈顶端,正如我们从dumpsys activity命令显示中看到的那样:

[java] view
plain copy

Task id #239  

  TaskRecord{428efe30 #239 A=com.thecheesefactory.lab.launchmode U=0 sz=2}  

  Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.thecheesefactory.lab.launchmode/.StandardActivity }  

    Hist #1: ActivityRecord{429a88d0 u0 com.thecheesefactory.lab.launchmode/.SingleTaskActivity t239}  

      Intent { cmp=com.thecheesefactory.lab.launchmode/.SingleTaskActivity }  

      ProcessRecord{42243130 18965:com.thecheesefactory.lab.launchmode/u0a123}  

    Hist #0: ActivityRecord{425fec98 u0 com.thecheesefactory.lab.launchmode/.StandardActivity t239}  

      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.thecheesefactory.lab.launchmode/.StandardActivity }  

      ProcessRecord{42243130 18965:com.thecheesefactory.lab.launchmode/u0a123}  

      如果你想让singleTask Activity像文档中描述那样工作:创建一个新的任务并将Activity作为任务的根Activity。你需要指定taskAffinity属性:

[java] view
plain copy

<activity  

            android:name=".SingleTaskActivity"  

            android:label="singleTask launchMode"  

            android:launchMode="singleTask"  

            android:taskAffinity="">  

      下面是我们尝试加载singleTaskActivity的结果:





      你需要根据Activity的表现来决定是否要指定taskAffinity属性。

      和另一个应用协作

      一旦Intent是来自另一个应用,并且系统中不存在一个响应Intent的Activity实例,系统会创建一个新的Task并将新建的Activity以根Activity的形式放在新建的Task中。





      除非存在一个这个应用的任务,并且这个任务是要调用的singleTask Activity的拥有者,这样的话新建的Activity将会被放在该任务的顶端。



      如果在任何一个任务栈中存在一个该Activity实例,整个任务将会被移动到顶端,并且singleTask Activity上面的每一个Activity都会被销毁掉。如果按下返回键,用户在返回到调用者之前必须经过任务栈中的所有Activity。



      使用该模式的例子就是任何入口点的Activity,例如email客户端的收件箱页面或者是社交网络应用的时间轴。这些Activity不允许有多个实例,所以singleTask模式非常适合。但是你需要明智的使用这种模式,因为Activities可能会在用户不知情的情况下被销毁。

singleInstance

      这个模式和singleTask很接近,系统中只能有一个Activity的实例。不同之处是,持有singleInstance Activity的任务栈只能有一个Activity。如果另一个Activity被singleInstance Activity调用,系统会自动创建一个新的Task来存放新建的Activity。同样,如果singleInstance
Activity被调用,也会创建一个任务来存放该Activity。
      从dumpsys提供的信息来看,系统中好像有两个任务,但是只有一个出现在任务管理器中,谁被显示依赖于那个是最近被移动到顶端的。这导致的结果就是,尽管有一个任务仍然在后台工作,但我们无法将其切换到前台。一点意义都没有。。。
      下图展示了当栈中已经存在一些Activity时,调用singleInstance Activity会发生什么:



      但是我们从任务管理器中看到的是如下界面:



      由于这个任务只能有一个Activity,我们不再也不能切换到Task#1了。唯一能做到的办法是重新启动应用,但看起来singleInstance任务反而又会被隐藏在后台。

      不过有解决该问题的办法。就像我们处理singleTask Activity一样,给singleInstance Activity指定一个taskAffinity属性就可以是的任务管理器中出现多个任务。
      

[java] view
plain copy

<activity  

            android:name=".SingleInstanceActivity"  

            android:label="singleInstance launchMode"  

            android:launchMode="singleInstance"  

            android:taskAffinity="">  



      这种模式很少使用。真正使用到的是Launcher的Activity或者你100%确保只有一个Activity的应用。不管怎样,我建议除非在必要的情况下否则不要使用该加载模式。

转载地址:http://blog.csdn.net/wqy19920429/article/details/45072779
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐