Activity启动模式图文详解:standard, singleTop, singleTask 以及 singleInstance

英文原文:Understand Android Activity's launchMode: standard, singleTop, singleTask and singleInstance  另外关于启动模式还有篇很好的文章:Android中Activity四种启动模式和taskAffinity属性详解  

Activity是安卓上最聪明的设计之一,优秀的内存管理让多任务完美运行在最流行的操作系统之上。并不是让Activity在屏幕上启动就完事了,其启动方式也是需要关注的。这个话题的内容很多,其中很重要的就是启动模式(launchMode)。这也是我们这篇博客要讨论的内容。

因为不同的Activity有不同的目的。有些被设计成每发送一个intent都单独一个Activity工作,比如邮件客户端中撰写邮件的Activity,而有些则被设计成单例的,比如邮件收件箱的Activity。

这就是为什么指明一个Activity是否需要新建还是使用现有Activity是很有必要的,否则可能导致糟糕的用户体验。多亏了安卓的核心工程师,让launchMode可以帮助你专门应对这种情况。

设置一个launchMode

一般地,我们可以直接在AndroidManifest.xml <activity>标签的一个属性中设置launchMode,如下:

  1. <activity
  2.             android:name=".SingleTaskActivity"
  3.             android:label="singleTask launchMode"
  4.             android:launchMode="singleTask">

有4种类型的launchMode,我们一个一个的看。

standard

这是默认的模式。

这种模式下,当Intent发送的时候,Activity总是被创建一个新的出来单独工作。想象一下,如果有发送10个撰写邮件的Intent,那么将有10个不同的Activity启动。


Lollipop之前设备上的表现


这种Activity将被创建并置于栈顶,和发送intent的Activity处于同一个任务中。注:一般来讲,安卓第三个虚拟键所列出的那些就是任务。

standardtopstandard


下面的图片显示了向标准启动模式的Activity分享照片时的情况。虽然分别来自不同的应用,但仍然它会和发送intent的Activity处于同一个任务中。

注:从图中可以看出分享图片的是Gallery应用。


standardgallery2


同时你会看到此时任务管理器是这样的(有一点怪异)。



gallerystandard



如果我们切换到另外一个应用然后再切回到Gallery,你会发现standard launchMode启动的Activity仍然在Gallery任务的上面,导致在操作Gallery之前,我们必须首先结束这个额外的Activity。


Lollipop设备上的表现


如果Activity都是来自同一个应用,其表现和Lollipop之前的设备一样,在任务的顶端。


standardstandardl


但是如果intent来自其他应用,将创建一个新的任务,同时新创建的Activity会被作为一个根Activity,如下:


standardgalleryl

注:图片中的Task#2和Task#3分别表示两个任务,序号大的比序号小的后启动。


下面是任务管理器中的样子:


gallerystandardl1


发生这种情况的原因是Lollipop中任务管理系统做了修改,让它看起来更合理了。因为它们在不同的任务中,你可以直接切回Gallery,你还可以触发另一个Intent,创建新的与之前相同的任务。


gallerystandardl2


撰写邮件的Activity或者发布社交网络状态的Activity都是采用这种Activity的例子。如果你希望Activity单独服务于一个Intent,就可以考虑standard启动模式。

singleTop

接下来就是singleTop模式。它的表现几乎和standard模式一模一样,一个singleTop Activity 的实例可以无限多,唯一的区别是如果在栈顶已经有一个相同类型的Activity实例,Intent不会再创建一个Activity,而是通过onNewIntent()被发送到现有的Activity。


singletop


在singleTop模式下我们需要同时在onCreate() 和 onNewIntent()中处理发来的intent,以满足不同情况。

这种启动模式的用例之一就是搜索功能。假设我们创建了一个搜索框,点击搜索的时候将导航到一个显示搜索结果列表的SearchActivity中,为了更好的用户体验,这个搜索框一般也会被放到SearchActivity中,这样用户想要再次搜索就不需要按返回键。

想像一下,如果每次显示搜索结果的时候我们都启动一个新的activity,10次搜索10个activity,那样当我们想返回最初的那个activity的时候需要按10次返回。

所以我们应该这样,如果栈顶已经有一个SearchActivity,我们将Intent发送给现有的activity,让它来更新搜索结果。这样就只会有一个在栈顶的SearchActivity,只需点一次back就可以回到之前的activity。

不管怎样,singleTop和它的调用者处在一个任务中。如果你想要让intent发送给另一个任务中处于栈顶的Activity,是不行的。

而当Intent来自于另外一个应用的时候,新的Activity的启动方式和standard模式是一致的(pre-Lollipop:处于调用者任务的栈顶,Lollipop:会创建一个新的任务)。

singleTask

这种模式和standard以及singleTop有很大不同。singleTask模式的Activity只允许在系统中有一个实例。如果系统中已经有了一个实例,持有这个实例的任务将移动到顶部,同时intent将被通过onNewIntent()发送。如果没有,则会创建一个新的Activity并置放在合适的任务中。


在同一个应用中的情况


如果系统中还没有singleTask的Activity,会新创建一个,并放在同一任务的栈顶。


singleTask1


但是如果已经存在,singleTask Activity上面的所有Activity将以合适的方式自动销毁,让我们想要显示的Activity处于栈顶。同时Intent也会通过onNewIntent()方法发送到这个singleTask Activity。


singleTaskD


在用户体验方面,可能不是很合理,但是它就是这样设计的...

你可能注意到了 官方文档 中提到的一个问题:

 系统会创建一个新的任务,并将这个Activity实例化为新任务的根部(root)。

但从实验结果来看,并不是这么回事。singleTask Activity仍然在任务的Activity栈顶,我们可以从dumpsys activity 命令显示上看出来:

  1. Task id #239
  2.   TaskRecord{428efe30 #239 A=com.thecheesefactory.lab.launchmode U=0 sz=2}
  3.   Intent
  4.  { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]
  5.  flg=0x10000000 
  6. cmp=com.thecheesefactory.lab.launchmode/.StandardActivity }
  7.     Hist #1: ActivityRecord{429a88d0 u0 com.thecheesefactory.lab.launchmode/.SingleTaskActivity t239}
  8.       Intent { cmp=com.thecheesefactory.lab.launchmode/.SingleTaskActivity }
  9.       ProcessRecord{42243130 18965:com.thecheesefactory.lab.launchmode/u0a123}
  10.     Hist #0: ActivityRecord{425fec98 u0 com.thecheesefactory.lab.launchmode/.StandardActivity t239}
  11.       Intent
  12.  { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]
  13.  flg=0x10000000 
  14. cmp=com.thecheesefactory.lab.launchmode/.StandardActivity }
  15.       ProcessRecord{42243130 18965:com.thecheesefactory.lab.launchmode/u0a123}


如果你希望singleTask Activity表现的和文档中描述的一致,你需要为singleTask Activity设置taskAffinity属性。

  1. <activity
  2.             android:name=".SingleTaskActivity"
  3.             android:label="singleTask launchMode"
  4.             android:launchMode="singleTask"
  5.             android:taskAffinity="">


这里是启动SingleTaskActivity的的结果(使用了taskAffinity之后)。


singleTaskTaskAffinity

screenshot17


是否使用taskAffinity取决于你自己。


和其他应用一起工作的情况


一旦intent是从另外的应用发送过来,并且系统中也没有任何Activity的实例,则会创建一个新的任务,并且新的Activity被作为根Activity创建。


singleTaskAnotherApp1

singletaskfromapp2


除非拥有这个singleTask Activity 的应用已经存在,那样的话,新建的Activity会置于这个任务的上面(而不是新建一个任务)。


singleTaskAnotherApp2

In case that there is an Activity instance existed in any Task, the whole Task would be moved to top and every single Activity placed above the singleTask Activity will be destroyed with lifecycle. If back button is pressed, user has to travel through the Activities in the stack before going back to the caller Task.

假设已经有了一个Activity的实例,不管它是在哪个任务中(包括上面的那种情况,在用于这个Activity的应用中),整个任务将被移到顶端,而singleTask  Activity上面的所有 Activity 都将被销毁, 用户需要按back键遍历玩栈中的Activity才能回到调用者任务。


singleTaskAnotherApp3


这种模式的应用案例有。邮件客户端的收件箱或者社交网络的时间轴。这些Activity一般不会设计成拥有多个实例,singleTask可以满足。但是在使用这种模式的时候必须要明智,因为有些Activity会在用户不知情的情况下被销毁。

singleInstance

这个模式非常接近于singleTask,系统中只允许一个Activity的实例存在。区别在于持有这个Activity的任务中只能有一个Activity:即这个单例本身。If another Activity is called from this kind of Activity, a new Task would be automatically created to place that new Activity. Likewise, if singleInstance Activity is called, new Task would be created to place the Activity.

不过结果却很怪异,从dumpsys提供的信息来看,似乎系统中有两个任务但任务管理器中只显示一个,即最后被移到顶部的那个。导致虽然后台有一个任务在运行,我们却无法切换回去,这一点也不科学。

下面是当singleInstance Activity被调用的同时栈中已经有一些Activity的情况下所发生的事情:

singleInstance


本来有两个任务,但是任务管理器中却只显示一个任务:


singleInstances

Since this Task could has only one Activity, we couldn't switch back to Task #1 anymore. Only way to do so is to relaunch the application from launcher but it appears that the singleInstance Task would be hidden in the background instead.

因为这个任务只有一个Activity,我们再也无法切回到任务#1了。唯一的办法是重新在launcher中启动这个应用。 but之后的没有翻译,因为我也不明白作者的意思。

不过这个问题也有解决方案,就像我们在singleTask Acvity中做的,只要为singleInstance Activity设置taskAffinity属性就可以了。

  1. <activity
  2.             android:name=".SingleInstanceActivity"
  3.             android:label="singleInstance launchMode"
  4.             android:launchMode="singleInstance"
  5.             android:taskAffinity="">

现在科学多了。

screenshot18


这种模式很少被使用。实际使用的案例如Launcher的Activity或者100%确定只有一个Activity的应用。总之除非完全有必要,不然我不建议使用这种模式。

Intent Flags

除了在AndroidManifest.xml中直接设置launch mode,我们还可以通过叫做 Intent Flags的东西设置更多的行为,比如:

  1. Intent intent = new Intent(StandardActivity.this, StandardActivity.class);
  2. intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
  3. startActivity(intent);

这段代码将会启动一个singleTop启动模式的的StandardActivity

有许多种Flag可以使用,更多的请参考Intent

希望这篇文章对你有用。

来源:网络


智能推荐

Activity的启动模式之singleTask模式

Activity在整个应用程序中只存在一个实例,每次启动该activity时,系统首先会检查栈中是否存在该活动的实例,如果发现已经存在则直接使用该实例,并将当前activity之上的所有activity出栈,如果没有发现则创建一个新的实例   点击mainActivity按钮,再点击BAty按钮 ,mainActivity只存在一个,而且点击back键后直接退出...

Android启动模式之singleInstance

刚学习Android之时,就知道Activity有四大启动模式: standard:标准模式,每次都会在栈里新建一个activity实例。 singleTask:当该activity实例已存在在栈内时,会复用其实例,并将其顶上的其余activity实例移除。 singleTop:当该activity已存在于栈顶时,则复用其实例。 singleTask:栈内永远就只有一个实例。 这是最开始对四大启动...

Activity启动模式standard和singleInstance相互跳转生命周期

       标题有点长哈,本人文学功底太差,实在想不出更简洁的标题,就这么凑合了。        本文内容为activity的启动模式实验中发现的一个与预期不符的小问题,然后经过进一步实验得出的个人结论的记录。希望各位大神看过后能给出指导。  &n...

singleTask 模式onNewIntent调用的时机以及Intent值的变化

现在 是三个activity ,MainActivity启动 BActivity(singleTask) ,BActivity启动 CActivity,CActivity还会载回到BActivity。 在这三个除了BActivity是singleTask外,其余都是standard, A-----B-------C---------B A------B 传递 B------C C--------B...

Activity启动模式之singleTask属性taskAffinity浅谈

  开篇知识点: activity在启动时,launchMode默认是standard模式,taskAffinity(任务相关性)默认是包名。 当为Activity指定launchMode为singleTask且taskAffinity为"xxx.xxx.xxx"时,系统会检测是否有同样的"xxx.xxx.xxx"存在,不存在,将会为它创建一个新的...

猜你喜欢

activity在SingleTask模式下的生命周期

这次是在SingleTask中作了实验。 我们先做了三个简单的activity。act_task1,act_task2,act_task3。 我们先作一般情况下跳转不同的act。如:act_task1->act_task2->act_task3发现与标准情况下没有任何不同。 接下来我们再回跳转到act_task1。发现如下: 系统会先从内部开始把act_task1与act_task3之...

解开Android应用程序组件Activity的"singleTask"之谜

出自:http://blog.csdn.net/luoshengyang/article/details/6714543 在Android应用程序中,可以配置Activity以四种方式来启动,其中最令人迷惑的就是"singleTask"这种方式了,官方文档称以这种方式启动的Activity总是属于一个任务的根Activity。果真如此吗?本文将为你解开Activity的&quo...

Android内存优化-方式四:谨慎使用SingleInstance模式

Android的单例模式在我们项目开发中经常会用到,不过使用的不恰当的话也会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长, 这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。 我们来分析一下,为什么会内存泄漏呢? AppManager appManager=AppManager.getInstance...

startActivityForResult启动singleTask的Activity,则onActivitResult()立即回调且resultCode为RE...

本文转自:http://blog.csdn.net/sodino/article/details/22101881   问题现象:           在刚安装完demo应用未登录任何帐号时,通过系统内的分享功能想将文件/图片等内容"发送给好友"或"发送到我的电脑",触发登录界面,但登录成功后,没有...

如何在winds局域网环境下建立网站

1.安装XAMPP,下载链接(https://www.apachefriends.org/index.html) 1.1选择winds10版本 2.安装XAMPP 2.1下载完毕后打开安装包安装一路确定有勾打勾,最后安装完毕 2.2安装完毕之后软件会跳出选择语言,选英语即可,然后会自动运行该软件,界面如下图所示(重启之后) 2.3打开界面之后会看到Modul列里有个apache服务,点击start...

问答精选

Long running we request, UI gets timed out by the time response is ready

I have one angular app and one spring boot app, there is one request goes from UI to spring boot with some 100-200 objects even more, for each of these objects another back end system is called within...

Binding <Print Screen> key to a function on Solaris

I found this code: Unfortunately, this works on a windows machine. I couldn't find a way to rebind the print screen key on Solaris. I found out that I needed to disable the shortcut from the "key...

jQuery - How do I add one to a variable when an input is entered?

The code is the following: It should add one for each input. Right now it stays the same. For example, when you answer "What is your name", it should change risk form 0 to 1. You have to add...

How can I send datas from sqlite3 to jade?

Ok, i know this is a stupid question but i'm new with node.js and jade and sqlite3... so i was wondering how can i send the database dates from a javascript scrit to jade template to populate a table....

What's the meaning of the percentages displayed for each test on PyTest?

I'm new to testing with Pytest, and I've run into a minor but annoying hangup. In the command line test session results, I see my tests passing, but the percentage shown is not 100%, for some tests. W...

相关问题

相关文章

热门文章

推荐文章

相关标签

推荐问答