很想弄清楚启动一个Activity和Task(任务)的关系,网上也有不少相关资料,由从源码来具体分析的,也有针对launchmode来分析,但都不是本身的,理解起来总不是那么容易,为此,尝试着本身去理解其中的逻辑。不过事先须要弄清楚两个问题:java
Android的framework框架中采用C/S的方式实现由后台服务ActivityManagerService(不少书上将其简称为Ams)来管理具体的Acitivity实例,虽然Ams命名为ActivityManagerService,可是它其实不只仅管理Activity,他还管理除Acitvity外的其它三大组件,还包括Intent、pendingintent、apk进程和task等等,具体能够查看源码frameworks\base\services\java\com\android\server\am下面的Ams源码和相关的*Record类。android
网上资料显示在2.3之后的SDK中,Ams将原来的HistoryRecord类从新命名为ActivityRecord类,并将其原来Ams中的一些处理抽出来成为一个ActivityStack类,每个Activity实例对应一个ActivityRecord对象,并存放在ActivityStack中,手机一旦启动,就会启动一个Ams服务,在Ams服务中有一个ActivityStack实例专门管理手机上的ActivityRecord实例。这样一来,不具体分析源码,仅从架构角度来讲谁负责管理Activity的问题就清晰多了。web
根据对上面问题的解答,既然Activity是由Ams经过ActivityStack来管理的,那么这个Task又是干什么的呢?架构
以往基于应用(application)的程序开发中,程序具备明确的边界,一个程序就是一个应用,一个应用为了实现功能能够采用开辟新线程甚至新进程来辅助,可是应用与应用之间不能复用资源和功能。而Android引入了基于组件开发的软件架构,虽然咱们开发android程序,仍然使用一个apk工程一个Application的开发形式,可是对于Aplication的开发就用到了Activity、service等四大组件,其中的每个组件,都是能够被跨应用复用的哦,这个就是android的神奇之处。app
另外值得一提的是,虽然组件能够跨应用被调用,可是一个组件所在的进程必须是在组件所在的Aplication进程中。因为android强化了组件概念,弱化了Aplication的概念,因此在android程序开发中,A应用的A组件想要使用拍照或录像的功能就能够不用去针对Camera类进行开发,直接调用系统自带的摄像头应用(称其B应用)中的组件(称其B组件)就能够了,可是这就引起了一个新问题,A组件跑在A应用中,B组件跑在B应用中,天然都不在同一个进程中,那么从B组件中返回的时候,如何实现正确返回到A组件呢?Task就是来负责实现这个功能的,它是从用户角度来理解应用而创建的一个抽象概念。由于用户所能看到的组件就是Activity,因此Task能够理解为实现一个功能而负责管理全部用到的Activity实例的栈。框架
其实查看源码,在Ams内部,并非真的有这么一个存放Activity的Task栈,Activity仍是经过ActivityStack来管理,在ActivityRecord中有一个TaskRecord对象记录了真实的Activity实例是属于哪一个Task的。Task经过一个int类型的TaskId来惟一标识,该值在手机重启时将会被置零。ide
说了这么多,仍是找一个Task任务最直观的体现吧。先重启手机,长按home键,发现弹出的最近任务中一个任务也没有,而后开启A应用,长按home键,会发现有一个A应用的任务,查看手机进程,应该尚未B进程的;在A应用的A组件中调B应用的B组件,此时看手机的进程,除了A进程外,还有个B的进程,可是长按home键,能看到的仍是只有一个A应用的任务。其实这个时候,B应用已经跑起来了,可是对用户来讲,他其实没有开启过B应用,因此Task任务自始至终都是从用户的角度出发而设计的概念,保证用户的调用逻辑。函数
理清楚了上述两个概念问题,进入Activity的启动与Task的关系以前,让咱们先来了解下启动一个Activity与apk Application和进程的关系。对Activity启动过程具体的分析,工程耗时很庞大,网上有个老罗整理的三篇博客,我看了半天仍是云里雾里的,有兴趣的能够查看以下连接:http://blog.csdn.net/luoshengyang/article/details/6685853。我经过我如今的认识和目前经过Demo的测试来看,启动一个Activity时,Ams首先会去查询该Activity所在的应用是否开启,若是没有开启则会启动一个进程去Run这个Application,所以不管经过Launcher仍是经过常规的程序内部调用startActivity来启动一个Activity,所要启动的Activity都是跑在其注册的apk所在的Application进程中(或者该组件android:process指定的进程中),而其TaskID通常是和启动它的组件所属的TaskId一直,可是也不尽然,这就要看下面的具体分析了。测试
另外经过对service的应用,能够得出结论,一个apk,即一个应用(Application)能够跑在多个进程中,一个进程在一个虚拟机中运行,也即一个apk能够启动多个虚拟机。动画
经过shareuserID能够将多个apk,跑在同一个进程中。
从而得出结论:一个虚拟机只能跑一个进程,一个进程里能够跑多个应用,一个应用也能够跑在多个进程中,这就是他们的关系。
启动一个Activity有两种方式,一种就是经过Launcher,另一种是经过程序代码调用startActivity函数实现(验证过AppWidget其实与这种方式是一致的)其实影响Activity启动关键点大体有三个因素:Activity注册信息中的launchMode、启动Activity时Intent中的launchFlags和Activity注册信息中的taskAffinity、allowTaskReparenting、clearTaskOnLaunch、alwaysRetainTaskState、finishOnTaskLaunch等信息。一般状况下,咱们只须要针对第一个因素进行合理设置就能知足咱们应用开发的需求了。
launchMode的四种方式
launchMode方法是在apk的manifest文件中针对每个Activity的android:launchMode属性进行设置的方式,共有四种模式能够设置,分别是standard、singleTop、singleTask、singleInstance,下面分别阐述之(因为其中几种因素有设置时,会影响lauchMode的四种模式,因此下面状况下,其它因素都是缺省不设置的状况)。
standard
standard是默认模式,即假设用户在manifest中对Activity不指定android:launchMode的状况下,缺省启动模式即为standard。在启动一个以standard为launchMode的Activity时,Ams只是简单的建立一个新的Activity实例,将其放到ActivityStack(为了行文方便,后面将ActivityStack简称AS),其TaskId则与启动这个Activity的调用者Activity相同(即就算建立一个新的进程,可是其TaskId仍是跟调用者一致的,以确保用户回退操做时保持一致)。这种是最多见的使用方式。
singleTop
启动一个以singleTop为lauchMode的Activity时,Ams会查询AS:假如在AS顶端正是要启动的Activity实例,那么Ams就不会从新启动一个Activity实例,而是调用AS栈顶的该Activity实例的OnNewIntent函数(天然不会修改原来的TaskId值);假如在AS栈顶不是该Activity的实例,那么就会建立一个新的实例,将其压入AS,其TaskId与调用者Activity相同。这种方式主要用于避免自调自过程当中,产生多个实例的状况。
singleTask
启动一个以singleTask为lauchMode的Activity时,Ams会查询AS:若是AS内有一个该类Activity的实例,那么就会将该实例置于TS的顶端(原来位于该实例上面的其它同TaskId的activity实例,将被销毁),并调用该实例的onNewIntent函数;若是AS内没有该类的实例,就会启动一个新的实例,将其压入AS,其TaskID与启动它的调用者没有必然关系,而是取决于该Activity所在apk进程是否有TaskId,假如没有就会建立一个新的TaskId。在实测中发现,若是singleTask模式启动的Activity是AS中同TaskId的最底部一个(或被称谓Task栈的根实例),那么在经过桌面长按,在近期任务中跳转到Activity所在的任务时,即便该Activity实例不是在栈顶,也会被置到栈顶(还会调用其onNewIntent函数),并将AS上同TaskId的其它Activity实例销毁,具体能够经过附带的demo来验证,其中TaskOne中的Activity1置成singleTask启动模式,其它均为默认的standard,其log输出以下:
//在TaskTwo中启动Activity1,此前TaskOne apk没有在运行,因此启动Activity1时会申请一个新的TaskId,2afcfbd8这个Activity1成为了TaskId为53的Task栈栈底实例
04-05 16:21:27.144: E/ActivityB @2b003230(17933): onPause pid 17933 taskid 52
04-05 16:21:27.404: E/Activity1 @2afcfbd8(18489): onCreate pid 18489 taskid 53
04-05 16:21:29.214: E/ActivityB @2afecaf0(17933): onCreate pid 17933 taskid 53
04-05 16:21:30.254: E/Activity2 @2afdeef0(18489): onCreate pid 18489 taskid 53
04-05 16:21:30.894: E/ActivityB @2aff9328(17933): onCreate pid 17933 taskid 53
04-05 16:21:31.454: E/Activity2 @2afe9330(18489): onCreate pid 18489 taskid 53
04-05 16:21:31.864: E/ActivityB @2b009910(17933): onCreate pid 17933 taskid 53
04-05 16:21:32.424: E/Activity2 @2aff2570(18489): onCreate pid 18489 taskid 53
//经过长按跳转到TaskOne任务,此时在AS中id为53的在Activity1上面有6个
04-05 16:22:35.144: E/ActivityB @2afecaf0(17933): onDestroy pid 17933 taskid 53
04-05 16:22:35.144: E/Activity2 @2afdeef0(18489): onDestroy pid 18489 taskid 53
04-05 16:22:35.234: E/ActivityB @2aff9328(17933): onDestroy pid 17933 taskid 53
04-05 16:22:35.254: E/Activity2 @2afe9330(18489): onDestroy pid 18489 taskid 53
04-05 16:22:35.324: E/ActivityB @2b009910(17933): onDestroy pid 17933 taskid 53
04-05 16:22:35.344: E/Activity2 @2aff2570(18489): onPause pid 18489 taskid 53
04-05 16:22:35.394: E/Activity1 @2afcfbd8(18489): onNewIntent pid 18489 taskid 53
04-05 16:22:35.394: E/Activity1 @2afcfbd8(18489): onStart pid 18489 taskid 53
04-05 16:22:35.394: E/Activity1 @2afcfbd8(18489): onResume pid 18489 taskid 53
04-05 16:22:35.524: E/Activity2 @2aff2570(18489): onStop pid 18489 taskid 53
04-05 16:22:35.524: E/Activity2 @2aff2570(18489): onDestroy pid 18489 taskid 53
singleInstance
启动一个以singleInstance为launchmode的Activity时,假如AS中已经有一个该类实例,那么调用其onNewIntent函数;不然就会建立一个新的TaskId,与该Activity所在的apk进程彻底不一样的TaskId,并且这个TaskId值之后也不会被用于其余任何Activity实例中。
简单小结
通常咱们开发普通的应用程序时,咱们只须要使用缺省的standard和singTop方式就够用了,不须要使用singleTask和singleInstance来声明注册的Activity,由于它将破坏用户感受上的回退操做,给用户使用上带来迷惑,因此通常将这二者用于很耗资源的Activity,经过查看源码发如今源码packages\apps中的程序,有以下一些应用使用了这二者高级设置
AndroidManifest.xml (packages\apps\browser): android:launchMode="singleTask"
AndroidManifest.xml (packages\apps\calendar): <activity android:name="AlertActivity" android:launchMode="singleInstance"
AndroidManifest.xml (packages\apps\contacts): android:launchMode="singleTask"
AndroidManifest.xml (packages\apps\deskclock): android:launchMode="singleInstance"
AndroidManifest.xml (packages\apps\deskclock): android:launchMode="singleInstance"
AndroidManifest.xml (packages\apps\deskclock): android:launchMode="singleInstance"
AndroidManifest.xml (packages\apps\email): android:launchMode="singleTask"
AndroidManifest.xml (packages\apps\launcher2): android:launchMode="singleTask"
AndroidManifest.xml (packages\apps\music): android:launchMode="singleTask"
AndroidManifest.xml (packages\apps\phone): android:launchMode="singleInstance"
AndroidManifest.xml (packages\apps\phone): android:launchMode="singleInstance">
AndroidManifest.xml (packages\apps\quicksearchbox): android:launchMode="singleTask"
AndroidManifest.xml (packages\apps\settings): android:launchMode="singleTask"
AndroidManifest.xml (packages\providers\downloadprovider): android:launchMode="singleTask"
在上述分析中没有将其它两个因素引入,主要是本身在这方面接触的也比较少,并且这三方面因素共同组合会产生不少种不一样效果,因此就没作具体展开。转载一些网友收集的资料以下:
android:allowTaskReparenting
用来标记Activity可否从启动的Task移动到有着affinity的Task(当这个Task进入到前台时)
“true”,表示能移动,“false”,表示它必须呆在启动时呆在的那个Task里。
若是这个特性没有被设定,设定到<application>元素上的allowTaskReparenting特性的值会应用到Activity上。默认值为“false”。
通常来讲,当Activity启动后,它就与启动它的Task关联,而且在那里耗尽它的整个生命周期。当当前的Task再也不显示时,你可使用这个特性来强制Activity移动到有着affinity的Task中。典型用法是:把一个应用程序的Activity移到另外一个应用程序的主Task中。
例如,若是 email中包含一个web页的连接,点击它就会启动一个Activity来显示这个页面。这个Activity是由Browser应用程序定义的,可是,如今它做为email Task的一部分。若是它从新宿主到Browser Task里,当Browser下一次进入到前台时,它就能被看见,而且,当email Task再次进入前台时,就看不到它了。
Actvity的affinity是由taskAffinity特性定义的。Task的affinity是经过读取根Activity的affinity决定。所以,根Activity老是位于相同affinity的Task里。因为启动模式为“singleTask”和“singleInstance”的Activity只能位于Task的底部,所以,从新宿主只能限于“standard”和“singleTop”模式。
android:alwaysRetainTaskState
用来标记Activity所在的Task的状态是否老是由系统来保持。
“true”,表示老是;“false”,表示在某种情形下容许系统恢复Task到它的初始化状态。默认值是“false”。
这个特性只针对Task的根Activity有意义;对其它Activity来讲,忽略之。
通常来讲,特定的情形如当用户从主画面从新选择这个Task时,系统会对这个Task进行清理(从stack中删除位于根Activity之上的全部Activivity)。典型的状况,当用户有一段时间没有访问这个Task时也会这么作,例如30分钟。
然而,当这个特性设为“true”时,用户老是能回到这个Task的最新状态,不管他们是如何启动的。这很是有用,例如,像Browser应用程序,这里有不少的状态(例如多个打开的Tab),用户不想丢失这些状态。
android:clearTaskOnLaunch
用来标记是否从Task中清除全部的Activity,除了根Activity外(每当从主画面从新启动时)
“true”,表示老是清除至它的根Activity,“false”表示不。默认值是“false”。
这个特性只对启动一个新的Task的Activity(根Activity)有意义;对Task中其它的Activity忽略。
当这个值为“true”,每次用户从新启动这个Task时,都会进入到它的根Activity中,无论这个Task最后在作些什么,也无论用户是使用BACK仍是HOME离开的。当这个值为“false”时,可能会在一些情形下(参考alwaysRetainTaskState特性)清除Task的Activity,但不老是。
假设,某人从主画面启动了Activity P,并从那里迁移至Activity Q。接下来用户按下HOME,而后返回Activity P。通常,用户可能见到的是Activity Q,由于它是P的Task中最后工做的内容。然而,若是P设定这个特性为“true”,当用户按下HOME并使这个Task再次进入前台时,其上的全部的Activity(在这里是Q)都将被清除。所以,当返回到这个Task时,用户只能看到P。
若是这个特性和allowTaskReparenting都设定为“true”,那些能从新宿主的Activity会移动到共享affinity的Task中;剩下的Activity都将被抛弃,如上所述。
android:finishOnTaskLaunch
用来标记当用户再次启动它的Task(在主画面选择这个Task)时已经存在的Activity实例是否要关闭(结束)
“true”,表示应该关闭,“false”表示不关闭。默认值是“false”。
若是这个特性和allowTaskReparenting都设定为“true”,这个特性胜出。Activity的affinity忽略。这个Activity不会从新宿主,可是会销毁。
android:launchMode
用于指示Activity如何启动。这里有四种模式,与Intent对象中的Activity Flags(FLAG_ACTIVITY_*变量)共同做用,来决定Activity如何启动来处理Intent。它们是:
"standard"
"singleTop"
"singleTask"
"singleInstance"
默认模式是“standard”。
前面文章:“Android四种Activity的加载模式”已经详细描述,这里就不作描述了.
android:noHistory
用于标记当用户从Activity上离开而且它在屏幕上再也不可见时Activity是否从Activity stack中清除并结束(调用finish()方法)——“true”,表示它应该关闭,“false”,表示不须要。默认值是“false”。
“true”值意味着Activity不会留下历史痕迹。由于它不会在Activity stack的Task中保留,所以,用户不能返回它。
好比启用界面的就能够借用这个。
android:taskAffinity
这就是本文所描述的任务共用性。
Activity为Task拥有的一个affinity。拥有相同的affinity的Activity理论上属于相同的Task(在用户的角度是相同的“应用程序”)。Task的affinity是由它的根Activity决定的。
affinity决定两件事情——Activity从新宿主的Task(参考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK标志启动的Activity宿主的Task。
默认状况,一个应用程序中的全部Activity都拥有相同的affinity。捏能够设定这个特性来重组它们,甚至能够把不一样应用程序中定义的Activity放置到相同的Task中。为了明确Activity不宿主特定的Task,设定该特性为空的字符串。
若是这个特性没有设置,Activity将从应用程序的设定那里继承下来(参考<application>元素的taskAffinity特性)。应用程序默认的affinity的名字是<manifest>元素中设定的package名。
FLAG_ACTIVITY_BROUGHT_TO_FRONT
这个标志通常不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定。
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_EXCLUDE_FROM_RECENTS
若是设置,新的Activity不会在最近启动的Activity的列表中保存。
FLAG_ACTIVITY_FORWARD_RESULT
若是设置,而且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个做为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity能够调用setResult(int),而且这个结果值将发送给那个做为答复目标的Activity。
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
这个标志通常不禁应用程序代码设置,若是这个Activity是从历史记录里启动的(常按HOME键),那么,系统会帮你设定。
FLAG_ACTIVITY_MULTIPLE_TASK
不要使用这个标志,除非你本身实现了应用程序启动器。与FLAG_ACTIVITY_NEW_TASK结合起来使用,能够禁用把已存的Task送入前台的行为。当设置时,新的Task老是会启动来处理Intent,而无论这是是否已经有一个Task能够处理相同的事情。
因为默认的系统不包含图形Task管理功能,所以,你不该该使用这个标志,除非你提供给用户一种方式能够返回到已经启动的Task。
若是FLAG_ACTIVITY_NEW_TASK标志没有设置,这个标志被忽略。
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_NO_ANIMATION
若是在Intent中设置,并传递给Context.startActivity()的话,这个标志将阻止系统进入下一个Activity时应用Acitivity迁移动画。这并不意味着动画将永不运行——若是另外一个Activity在启动显示以前,没有指定这个标志,那么,动画将被应用。这个标志能够很好的用于执行一连串的操做,而动画被看做是更高一级的事件的驱动。
FLAG_ACTIVITY_NO_HISTORY
若是设置,新的Activity将再也不历史stack中保留。用户一离开它,这个Activity就关闭了。这也能够经过设置noHistory特性。
FLAG_ACTIVITY_NO_USER_ACTION
若是设置,做为新启动的Activity进入前台时,这个标志将在Activity暂停以前阻止从最前方的Activity回调的onUserLeaveHint()。
典型的,一个Activity能够依赖这个回调指明显式的用户动做引发的Activity移出后台。这个回调在Activity的生命周期中标记一个合适的点,并关闭一些Notification。
若是一个Activity经过非用户驱动的事件,如来电或闹钟,启动的,这个标志也应该传递给Context.startActivity,保证暂停的Activity不认为用户已经知晓其Notification。
FLAG_ACTIVITY_PREVIOUS_IS_TOP
If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. The previous activity will be used as the top, with the assumption being that the current activity will finish itself immediately.
FLAG_ACTIVITY_REORDER_TO_FRONT
若是在Intent中设置,并传递给Context.startActivity(),这个标志将引起已经运行的Activity移动到历史stack的顶端。
例如,假设一个Task由四个Activity组成:A,B,C,D。若是D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,如今的次序变成A,C,D,B。若是FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被忽略。
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_SINGLE_TOP
若是设置,当这个Activity位于历史stack的顶端运行时,再也不启动一个新的。