Android下的任务和Activity栈

Task 

1、   什么是 Task ? 

Task 翻译成中文叫做任务,那么什么是任务呢? 

Task 就是一个栈 (A task is a stack of activities.) ,这个栈里面存放了很多 Activity ,它遵循着后进先出的原则。 

栈有两个动作:压栈(把对象压入到栈当中)和弹栈(把栈中的第一个对象从栈里面拿出来)。 

2、   Task 运行过程 

示例 1 :创建一个 Android应用程序,编写 3 个 Activity(FirstActivity 、 SecondActivity 、 ThirdActivity) ,每个 Activity 都有一个按钮,当点击 FirstActivity 的 Button 按钮时,跳转到 SecondActivity ,当点击 SecondActivity 的 Button 按钮时,跳转到 ThirdActivity ,当再点击 ThirdActivity 的 Button 按钮时,调用 Android 自带的一个发送短信的应用程序。(程序已打包上传至附件中,有需要的可下载进行查看运行) 

运行程序,观察 Task 运行的过程。 

这个应用程序的 FirstActivity 、 SecondActivity 、 ThirdActivity 以及 Android 自带的发送短信的应用程序就组成了一个 Task 。 

Task 运行过程讲解: 

1)   应用程序启动之后,运行的第一个 Activity ( FirstActivity ),该 FirstActivity 对象被压入到 Stack 当中。 

2)   当点击了 FirstActivity 的按钮之后,启动第二个 Activity ( SecondActivity ),该 SecondActivity 对象被压入到 Stack 当中。 

注:现在 FirstActivity 处在 Stack 的底部, SecondActivity 处于 Stack 的顶部,手机永远显示的都是 Stack 顶部的元素,所以现在界面显示的是 SecondActivity 。 

3)   当点击了 SecondActivity 的按钮之后,启动第三个 Activity ( ThirdActivity ),该 ThirdActivity 对象被压入到 Stack 当中。 

注:现在 FirstActivity 处在 Stack 的底部, ThirdActivity 处于 Stack 的顶部,手机永远显示的都是 Stack 顶部的元素,所以现在界面显示的是 ThirdActivity 。 

4)   最后点击 ThirdActivity 的按钮之后,启动 Android 自带的发送短信的应用程序( SMS Activity 对象),该 SMS Activity 对象被压入到 Stack 当中。 

注:同理,现在 FirstActivity 仍然处在 Stack 的底部, SMS Activity 对象处于 Stack 的顶部,所以现在界面显示的是 SMS Activity 对象的界面。 
(以上执行的是压栈操作,现在我们点击返回来看弹栈操作) 


5)   点击模拟器右侧的 Back 按钮之后,这个时候 SMS Activity 对象被从栈中弹出来。 SMS Activity 对象被弹出来之后, ThirdActivity 又变成栈的顶部,当前程序显示的就是 ThirdActivity 的内容。(因为栈遵循后进先出的原则) 

6)   当再点击 Back 按钮之后, ThirdActivity 被从栈中弹出来,程序显示 SecondActivity 的内容。 

7)   再点击 Back 按钮, SecondActivity 被从栈中弹出来,程序显示 FirstActivity 的内容。 

注意: 

a )、现在在 SecondActivity 中的 startActivity(intent) 后调用 finish() 方法。将程序运行到 ThirdActivity 后,点击 Back 按钮,此时,程序并没有返回到 SecondActivity ,而是直接返回到 FirstActivity 。 

原因:由于在点击 SecondActivity 的按钮之后,它启动了 ThirdActivity ,同时将 SecondActivity 销毁掉了,这个时候栈里面只有 ThirdActivity 和 FirstActivity ,当再点击 Back 的时候它就不是回到 SecondActivity 而是回到 FirstActivity 了。 

       b )、在整个栈当中, Activity 只有弹出和压入这两个动作,是不允许调换 Activity 之间的顺序的。 

3、   Task 对的作用 

在同一个 Task 里面的 Activity 被组织成同一个单元(可以将不同应用程序的 Activity 组织在一起)。

===================================================================

Java代码
  1. Intent intent = new Intent();     
  2. intent.setAction(Intent.ACTION_CALL);     
  3. intent.setData(Uri.parse("tel:" + number));     
  4. startActivity(intent);    


上面的这段代码就是在一个activity里通过Intent启动另一个activity的实例。

就像前面提到的,一个activity可以启动另一个,包括那些定义在不同应用程序中的。
一个activity就是一个用户界面,可以连续启动很多activity以提高用户体验。当然这个数量是有限的,这要根据手机的硬件配置来决定。上一篇文章中已经介绍,所有打开的activity都是存储在栈中的,所以如果打开的activity过多,会消占去很多的内存和处理器资源,严重时会导致当前应用无法启动或者系统崩溃。
activity是和任务紧密联系的。因为在Android系统中,为了保持用户体验,用户想做某件事情是以任务的结构作为逻辑,以应用的形式来表现的。而一个应用又可以包含很多的activity,所以就涉及到activity和task的关系问题。
note:一个task由很多的activity组成。
为了更好的说明举个例子:
TASK1包含三个activity分别是:p,q,r;启动顺序为p-->q-->r
TASK2包含两个activity分别是:a,b;启动顺序为a-->b;
TASK1首先启动p,那么这个p就是执行这个任务的根activity,任务也是定义在这个activity下的。通过属性Manifest文件中的activity属性中的taskAffinity来定义。
        (1)此时p启动后就被压入堆栈,因为此时堆栈中只有这一个activity,所以p处于栈底,也处于栈顶。此时用户能够看到的就是p界面。
        (2)p启动q后,q入栈。此时栈顶为q。q呈现的界面将p覆盖,p进入后台运行(此时是activity生命周期的pause状态)或者进入后台但不运行(此时是activity生命周期的stop状态)。
        (3)q启动r后,r入栈。此时栈顶为r。r呈现的界面将q覆盖,q进入后台运行。
        (4)按back键后就是调用finish函数结束r (activity)的生命。此时r出栈,r的生命周期结束。
        (5)继续back键,结束q的生命,此时q出栈。
        (6)继续back键,结束p的生命,此时p出栈。
        (7)此时栈内所有activity都已出栈,所以就会显示HOME屏幕界面.
上面的例子已经很明确,如果还不理解请看下面:
1、activity简单解释
简单的的说,任务就是用户所体验到的“应用程序”。它是一组相关的activity,分配到 一个栈中。栈中的根activity,是任务的开始——一般来说,它是用户组应用程序加载器中选择的activity。在栈顶的activity正是当前 正在运行的——集中处理用户动作的那个。当一个activity启动了另外一个,这个新的activity将压入栈中,它将成为正在运行中的 activity。前一个activity保留在栈中。当用户按下后退按键,当前的这个activity将中栈中弹出,而前面的那个activity恢复 成运行中状态。
为了更好的理解上面的例子要补充的是:
2、activity的生命周期:
onCreate()------->onStart()-------->onResume()--------->onSaveInstanceState()----->onPause()------->onStop
--------->onDestroy().

3、activity的各种状态的转换:


继续看上面的例子:上面的每一个activity都经历了这样一个过程的大部分。当启动一个activity时,首先是在onCreate处进行初始化界面所需要的信息,如:界面的views,buttons,分配的引用变量等;初始化完成后就会调用onStart,此时就可以看到界面了;当用户与界面进行交互时就会调用onResume函数;当此activity因为被其他activity没有完全覆盖而进入pause状态时就调用onPause(),当被其他activity完全覆盖时就调用onStop函数。在后面的这两种情况下都会调用onSaveInstanceState方法来暂时保存被覆盖的activity的状态,在这些被覆盖的activity重新回到界面上的时候会恢复这些状态;当调用finish方法使,这个activity就被destroy了,也就从栈中移除了。
         
    一个任务中的所有activity一起作为一个单元。整个任务(整个activity栈)可以移动到前台或者后台.假设,例如,当前的任务有四个 activity在栈中——三个在当前的activity之下。用户按下了HOME键,进入了应用程序加载器,选择了一个新的程序(实际上,是一个新的任务)。当前的任务进入了后台,新任务的根activity显示出来。然后,过了一会,用户退回到主界面,又重新选择了前一个应用程序(前一个任务),栈中有四个activity的那个任务,现在出现在了前台。当用户按下BACK按键,屏幕就不会再显示用户刚刚离开的那个activity,而是删除栈顶的 activity,同任务中的前一个activity将被显示出来。

     刚才说明的那些行为,是activity和任务的默认行为。但是有也办法修改它的所有方面。activity和任务的关联,activity在任务中的行为,受控于启动activity的行为对象的标志位和清单文件中的<activity> 元素的属性的互相作用。请求者和相应着都要说明发生了什么。


   在这里,主要的行为标志为是:

FLAG_ACTIVITY_NEW_TASK  
FLAG_ACTIVITY_CLEAR_TOP  
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED  
FLAG_ACTIVITY_SINGLE_TOP


       主要的<activity> 属性是:

taskAffinity  
launchMode  
allowTaskReparenting  
clearTaskOnLaunch  
alwaysRetainTaskState  
finishOnTaskLaunch


       下面一节将说明这些标志和属性都有什么用,他们之间怎么互相影响,应该用什么样的方案来控制它们的使用。


亲和性和新任务


        默认情况下,应用程序中的所有activity,都有一个对于其它activity的亲和性—这是一个对于同一个任务中的其他activity的优先权,然后,通过  <activity>元素的 taskAffinity 属性可以可以分别为每一个activity设置亲和性。不同应用程序定义的activity可以共享同一个亲和性,或者同一个应用程序定义的 activity可以指定不同的亲和性。亲和性在两种情况下发挥作用:当行为对象启动了一个包含 FLAG_ACTIVITY_NEW_TASK标志的activity,和当一个activity的allowTaskReparenting 属性设置为“true”。


 FLAG_ACTIVITY_NEW_TASK  标志 
       

        正如前面描述的,一个新的activity,默认情况下,被加载进调用startActivity()方法的activity对象所在的那个任务中。它被压入和调用者所在的同一个栈中,但是,如果行为对象在调用startActivity()方法时传递了FLAG_ACTIVITY_NEW_TASK标记,系统将用一个不同的任务来容纳这个新的activity。通常,就像这个标记的名字所代表的。它是一个新任务,但是,它不必非要这样。如果已经存在一个和这个activity亲和性相同的任务,这个activity就会载入到那个任务中,如果不是的话,才会启动新任务。


allowTaskReparenting  属性


        如果activity的allowTaskReparenting 属性设置为“true”,它就能从他启动时所在的任务移动到另一个出现在前台的任务。例如,假设有一个activity可以根据选择的城市包括天气情况,它作为一个旅行应用程序的一部分。它和同一个应用程序中的其他activity有同样的亲和性(默认的亲和性)并且允许重组。你的一个activity开启了天气报告器,所以它属于同一个任务中的这个activity,然而,当旅行应用程序开始运行时,天气报告器将被重新分配并显示到那个任务中。


启动模式


有4中不同的启动模式可以分配给 <activity> 元素的  launchMode  属性。


"standard" (默认的模式)  
"singleTop "  
"singleTask"  
"singleInstance"


这些模式主要区别在以下四点:

(1)哪个任务存放着activity,用来对行为进行响应。 对“standard ”和“singleTop ”模式来说,这个任务是产生行为(并且调用startActivity() )的那个——除非行为对象包含了 FLAG_ACTIVITY_NEW_TASK 标记。在这种情况下,像前面那节Affinities and new tasks  描述的一样,将会选择一个不同的任务。

(2)它们是否可以有多个实例。 "standard "和“singleTop ”类型的activity可以被实例化多次。它们可以属于多个任务,一个特定的任务也可以拥有同一个activity的多个实例。
作为比较"singleTask "和"singleInstance "类型的activity只限定有一个实例。因为这些activity是任务的根。这个限制意味着,在设备上不能同时有超过一个任务的实例。

(3)是否能有其他的activity在它所在的任务中。"singleInstance " 类型的activity是它所在任务中唯一的activity。如果它启动了其他的activity,不管那个activity的启动模式如何,它都会加载到一个不同的任务中——好像行为对象中的FLAG_ACTIVITY_NEW_TASK 标记。在其他的方面,"singleInstance "和"singleTask "模式是相同的。
其他三种模式运行任务中有多个activity。"singleTask "总是任务中的根activity,但是它可以启动其他的activity并分配到它所在的任务中。"standard "和"singleTop "类型的activity可以出现在任务中的任何地方。

(4)是否启动一个新的实例来处理一个新的行为。 对默认的"standard "模式来说,对于每一个行为都会创建一个新的实例来响应。每个实例只处理一个行为。对于"singleTop "模式,如果一个已经存在的实例位于目标任务activity栈的栈顶,那么他将被重用来处理这个行为。如果它不在栈顶,它将不会被重用,而是为行为创建一个新的实例,并压入栈中。

例如,假设,一个任务的activity栈由根activity A和 B,C,D从上到下按这样的顺序组成,所以这个栈就是A-B-C-D。一个行为指向类型为D的activity。如果D是默认的"standard "加载模式,一个新的实例会被启动,栈现在就是这样A-B-C-D-D。但是,如果D的加载模式是"singleTop ",已经存在的实例会用来处理这个行为(因为它在栈的顶端)并且栈中还应该是A-B-C-D。

   在前面提到,"singleTask "和"singleInstance "类型的activity最多只有一个实例,所以他们的实例应该会处理每个新的行为。"singleInstance "类型的activity总是在栈的顶端(因为他是任务中唯一的一个activity),所以总是能够适当的处理行为。然而,"singleTask "类型的activity也许会有其他的activity在它的上面。如果是这样的话,那就不能处理这个行为,这个行为被丢弃。(即使这个行为被丢弃了,它的到来也会导致那些应该保留不变任务显示到前台来)。


   当一个activity被要求处理一个新的行为时,行为对象会通过调用activity的 onNewIntent()  方法传递进来(最初启动activity的行为可以通过调用getIntent() 方法获得)。


       注意,当创建一个新的activity实例来处理一个新的行为时,用户总是能够通过按下BACK按键退回到前面的状态(前一个activity)。但是当一个已经存在的activity实例处理一个新的行为时,用户不能通过按下BACK按键退回到前面的状态。

      更多关于加载模式的内容,请看关于 <activity>  的描述。


清理栈


     如果用户离开一个任务很长时间。系统将清除除了根activity之外的所有activity。当用户重新回到任务中时,像是用户离开了它,除了只有最初的activity还在。这个理念是这样的,过了一段时间,用户很可能放弃之前所做的事情,回到任务去做一些新的事情。

     这只是默认情况,有一些activity的属性可以控制和修改它。

 alwaysRetainTaskState  属性


       如果一个任务的根activity的这个属性设置成了"true",那么刚才提到的那些默认行为就不会发生。这个任务保留所有的activity,甚至经过了很长一段时间。

 clearTaskOnLaunch  属性 
        
       如果任务的根activity的这个属性设置成了”true“,那么只要用户离开了任务并返回,就会清除除了根activity之外的所有activity。换句话说,它和alwaysRetainTaskState正好相反,当用户返回到任务时,总是恢复到最初的状态,不管离开了多长时间。


 finishOnTaskLaunch  属性


这个属性和clearTaskOnLaunch类似,但是它作用于单个activity,而不是整个任务。它可以导致任何的activity离开,包括根activity。当它设置成"true"的时候,作为任务一部分的activity只对当前会话有效。如果用户离开然后返回到任务中。它将不再出现。


       还有其他的方法强制将activity从栈中移除。如果一个行为对象包含了 FLAG_ACTIVITY_CLEAR_TOP  标志,它的目标任务中已经有了一个这样类型的activity实例,所有栈中位于这个实例之上的activity都会被清除,所以这个实例就会出现在栈顶并且对行为进行响应。如果activity被设计成"standard"模式,它也将会从栈中被清除,并且会启动新的实例来处理到来的行为。这是因为当设置成”standard“模式后,对每个新的行为都会创建一个新的实例。


      FLAG_ACTIVITY_CLEAR_TOP经常 和FLAG_ACTIVITY_NEW_TASK一起使用。当一起使用时,这些标志是定位一个在另一个任务中存在的activity并且将它放在一个可以响应行为的地方的一种方法。


启动任务


    Activity通过将行为过滤器”android .intent.action.MAIN“设置为指定动作和"android .intent.category.LAUNCHER"作为指定类型,来成为任务的入口。(前面关于行为过滤器那一些讨论的例子)。这种类型的过滤器会让activity的图标和标签显示在应用程序加载器上面,可以让用户启动和返回activity。


    第二个能力更为重要,用户应该可以在离开一个任务一段时间后返回。因为这样,能够初始化任务的"singleTask"和"singleInstance"模式,只能够用在那些拥有MAIN 和LAUNCHER 过滤器的activity中。想像一下如果没有这两个过滤器会发生什么:一个行为启动了"singleTask"模式的activity,启动了一个新的任务并且用户花了一些时间在这个任务上。然后用户按下了HOME键,这个任务被隐藏到了后台。因为没有在应用程序加载器上显示它,所以就没有办法返回到这个任务。


       一个类似的麻烦事 FLAG_ACTIVITY_NEW_TASK 标志。如果这个标志导致activity启动了一个新任务,并且用户按下HOME键离开了它,必须有一些方法将用户引导回它。一些实体(像是通知管理器) 总是在一个外部的任务中启动activity,而不作为它们的一部分,所以他们总是将带有FLAG_ACTIVITY_NEW_TASK 标记的行为对象传递到startActivity() 。如果你有一个可以被外部实体使用这个标签调用的activity,要注意用户应该有办法返回到启动的任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值