Tasks and Back Stack

大多数操作系统,在应用程序所寄存的可执行程序映像(如Windows系统里的.exe)、它所运行的进程以及和用户交互的图标和应用之间有一种严格的1对1关系。在Android系统里,这些关联要松散得多。并且重要的是要理解各种概念怎么样组成整体,这个主要是android的Mash-Up的思想。所谓Mash-Up,就是把写应用搞成搭积木,要出效果的时候,东家一块西家一块现场拼起来就好。这里面关键有两点,一个是模块化,另一个就是动态性。所谓模块化,就是一个应用的功能要明确的被封成一个个边界清晰的功能点,每一个功能点都像是一个黑盒,由预先定义的规则描述出其交互方式;而动态性,就是这些独立的模块能够在运行的时候,按照需求描述,连接在一起,共同完成某项更大的功能。在这两点上,Android都做得非常出色。
由于Android应用固有的灵活性,当实现这些不同方面的时候有一些基本术语需要加以理解:
一个Android包 (.apk)文件,其中包含一个应用程序的代码和资源。这是应用程序分发和下载的文件,用户用来安装该应用程序在他们的设备上。
一个任务一般而言是指用户视为的一个可启动应用程序:通常任务在桌面(home screen)有一个可访问的图标,且可以被切换到前台。
一个进程是一个运行着应用程序代码的底层核心过程。通常所有.apk里的代码运行在一个专有的进程里。不过,进程标记也可以用来限定代码运行位置,或者为整个.apk或者为个别的活动activity,接收者receiver,服务或提供者provider,组件。


Task,简单的说,就是一组以栈的模式聚集在一起的Activity组件集合。系统桌面是大多数task的起始的地方。当用户点击一个应用程序的图标时,这个应用程序的task就会来到最前端,优先级变为foreground。如果没有task对应到当前的应用程序,那么将会新建一个task,而且“main” activity就会被作为root activity在堆栈中。


同一时刻可以有多个Task在后台background。但是如果此时内存不足的话,系统会回收这些background的activities,所以要注意状态的保存和恢复。注意实现onSaveInstanceState()等这些回调方法。


一般情况下来讲Task中的activities是遵循一个堆栈的规则的,后进先出。且顺序是不可以被改变的。下面我们来看看特例们,都是哪些来影响这个堆栈的规则。

Android为了使我们能够打破默认的堆栈的先后出的模式,提供了两个种方式:一种是在AndroidManifest.xml定义Activity时指定它的加载模式,另一种是在用Intent开启一个Activity时,在Intent中加入标志。如果两种方式都用了,则后者的优先级更高。两种方式的差别在于,前者在于描述自己,向别的Acttivity等声明你们如何来加载我;而后者则是动态的,指出我要求你(要启动的Activity)如何来加载。

配置的主要方向有两个:一则是破坏已有栈的进出规则,或样式;另一则是开辟新Task栈完成本应在同一Task栈中完成的任务。

一些基本的<activity>标签如下:

一些基本的Flag有:

首先看看launchMode中的四个选项:

1,"standard" (the default mode)

缺省值。系统可以创建一个新的activity的实例依据于谁start了它,并且遵循那个intent中的flag规则。这个activity可以被实例化多次,每个实例可以属于不同的task的,并且一个task也可以由多个实例。对于使用频度一般开销一般什么都一般的Activity而言,standard模式无疑是最合适的,因为它逻辑简单条理清晰,所以是默认的选择。


2,"singleTop" “拒绝堆叠”

而singleTop模式,基本上于standard一致,仅在请求的Activity正好位于栈顶时,有所区别。此时,配置成singleTop的Activity,不再会构造新的实例加入到Task栈中,而是将新来的Intent发送到栈顶Activity中,栈顶的Activity可以通过重载onNewIntent来处理新的Intent(当然,也可以无视...)。这个模式,降低了位于栈顶时的一些重复开销,更避免了一些奇异的行为(想象一下,如果在栈顶连续几个都是同样的Activity,再一级级退出的时候,这是怎么样的用户体验...),很适合一些会有更新的列表Activity展示。一个活生生的实例是,在Android默认提供的应用中,浏览器(Browser)的书签Activity(BrowserBookmarkPage),就用的是singleTop。
singleTop模式,虽然破坏了原有栈的逻辑(复用了栈顶,而没有构造新元素进栈...),但并未开辟专属的Task。而singleTask,和singleInstance,则都采取的另辟Task的蹊径。标志为singleTask的Activity,最多仅有一个实例存在,并且,位于以它为根的Task中。所有对该Activity的请求,都会跳到该Activity的Task中展开进行。


以上说的两种加载模式,Activity均可以实例化多次,而下面讲的两个加载模式就只可以实例化一次。

3,"singleTask"“独立门户”

标志为singleTask的Activity,最多仅有一个实例存在,并且,位于以它为根的Task中。所有对该Activity的请求,都会跳到该Activity的Task中展开进行。singleTask,很象概念中的单件模式,所有的修改都是基于一个实例,这通常用在构造成本很大,但切换成本较小的Activity中。在Android源码提供的应用中,该模式被广泛的采用,最典型的例子,还是浏览器应用的主Activity(名为Browser...),它是展示当前tab,当前页面内容的窗口。它的构造成本大,但页面的切换还是较快的,于singleTask相配,还是挺天作之合的。同一个应用中调用该Activity时,如果该Activity没有被实例化,会在本应用程序的Task内实例化,如果已经实例化,会将Task中其上的Activity销毁后,调用onNewIntent;其它应用程序调用该Activity时,如果该Activity没有被实例化,会创建新的Task并实例化后入栈,如果已经实例化,会销毁其上的Activity,并调用onNewIntent。一句话,singleTask就是“独立门户”,在自己的Task里,并且启动时不允许其他Activity凌驾于自己之上


4,"singleInstance". "孤独寂寞"

相比之下,singleInstance显得更为极端一些。在大部分时候singleInstance与singleTask完全一致,唯一的不同在于,singleInstance的Activity,是它所在栈中仅有的一个Activity,如果涉及到的其他Activity,都移交到其他Task中进行。这使得singleInstance的Activity,像一座孤岛,彻底的黑盒,它不关注请求来自何方,也不计较后续由谁执行。加载该Activity时如果没有实例化,他会创建新的Task后,实例化入栈,如果已经存在,直接调用onNewIntent,该Activity的Task中不允许启动其它的Activity,任何从该Activity启动的其他Activity都将被放到其他task中,先检查是否有本应用的task,没有的话就创建。


用intent的Flag

1,FLAG_ACTIVITY_NEW_TASK

Start the activity in a new task. If a task is already running for the activity you are now starting, that task is brought to the foreground with its last state restored and the activity receives the new intent in onNewIntent().和singleTask一样。

2,FLAG_ACTIVITY_SINGLE_TOP

If the activity being started is the current activity (at the top of the back stack), then the existing instance receives a call to onNewIntent(), instead of creating a new instance of the activity.和singleTop一样。

3,FLAG_ACTIVITY_CLEAR_TOP

如果已经有这个对于的acitivity的task,这个flag会清理其上面的activities,并且调用的onNewIntent()方法。没有和它对应的launchMode。

这个flag经常和FLAG_ACTIVITY_NEW_TASK一起使用。


处理taskAffinity,Activity的归属

taskAffinity,是一种物以类聚的思想,它倾向于将taskAffinity属性相同的Activity,扔进同一个Task中。上述的launchMode或者Flags都是想如何处理task的堆栈,而taskAffinity是当这个acitivity启动的时候,它自己愿意呆在哪个task中。默认的是application的packageName。可以针对<application>或者组件级别的<activity>来设置taskAffinity。不同工程下的acitivyties可以设置成相同的taskAffinity。但是它的局限性比较大,只有在Flag为 FLAG_ACTIVITY_NEW_TASK或者"allowTaskReparenting "为true时。

当 FLAG_ACTIVITY_NEW_TASK时,代表新启动的activity,但是此时此acitivity没有实例,需要重新启动一个task,但是如果有个task中的taskAffinity一致,那么就启动一个activity,并融入到该task中。

allowTaskReparenting时,如果有个activity,已经启动在某个background的task中了,但是此时有个和它一致taskAffinity的task到foreground,那么它也会move过来。

前者属于刚出道就找到组织了,后者与潜伏了好久终于找到组织了。


合并起来的结论:

SingleTask

 当一个应用程序加载一个singleTask模式的Activity时,首先该Activity会检查是否存在与它的taskAffinity相同的Task。
    1、如果存在,那么检查是否实例化,如果已经实例化,那么销毁在该Activity以上的Activity并调用onNewIntent。如果没有实例化,那么该Activity实例化并入栈。
    2、如果不存在,那么就重新创建Task,并入栈。
用个流程来看就是:


具体在activity中查看的方法

用Thread.currentThread().getId()和getTaskId()获取到得线程id和taskid都是相同的,这说明同一个application下的activity是在一个线程中的。另外,查看当前运行的task堆栈

	        ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
	        List<RunningTaskInfo> lists = am.getRunningTasks(10);
	        for(int i = 0; i< lists.size(); i++){
	        	Log.e(TAG, i+">>"+lists.get(i).numActivities+ ">>" +lists.get(i).baseActivity.getClassName() + ">>" +lists.get(i).id);
	        }

拿到当前显示给用户的activity。

ComponentName cn = am.getRunningTasks(1).get(0).topActivity;  
Log.d("", "pkg:"+cn.getPackageName());  
Log.d("", "cls:"+cn.getClassName()); 



清理堆栈

当一个用户离开一个task足够久的时间,那么系统会清理掉出了root activity之外的其他的activities,节省空间,下面有几个参数可以改变这个设置。

1,alwaysRetainTaskState

这个设置为true的话,所有的activity都不会被清理。系统会把所有的activities都保存在堆栈中。

2,clearTaskOnLaunch

如果这个属性在一个task的root activity被设置为true,那么这个堆栈会被清理到root activity只要用户离开这个任务并且重新回来的时候,注意,是再重新回来的时候。往往 android:clearTaskOnLaunch="true" 是一个主入口,而且是耗资源比较大的,比如说拍照。

A -> B,  需要在A中设置上clear
效果就是当用户停留在B中, 按home键 (back键, 默认就是destroy了), B activity 将会被stop, 不会destroy
当继续运行程序, 进入到这个stack中, 系统会会根据这个clearTaskOnLaunch值, 来决定,是销毁除root activity以外的activity 还是 返回当前stack最顶层的activity

3,finishOnTaskLaunch

这个属性挺像 clearTaskOnLaunch的,只不过它是针对单个activity,而不是整个的task的。它可以导致任何的acitivity离开,甚至是root activity。如果设置为true,用户离开这个任务并且重新回来的时候,这个task中的那个activity已经被清理了。


下面有些说法,尚未验证:

  关于android:clearTaskOnLaunch使用测试,有两种情况 
  Activity A(启动界面.android:clearTaskOnLaunch = true) 
  Activity B 

  启动A后,再点击按钮启动B,点击home键,返回到home screen 
  a.长按home键,在recent中点击应该图标,显示B界面 
  b.再点击应用图标,显示A界面


开始一个任务:

<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>
这个类型的intentfilter会在launcher界面有个icon,给用户一个方式来启动这个activity和在任意时候重新回到那个它创建的task。

第二点要注意的是,用户可以离开task(Home键),也要有方法可以重新回到这个task。主要要注意的是"singleTask" 和 ""singleInstance",很容易形成孤岛,要么是上述的那类启动的activity,或者有notification等。或者给activity设置一个 finishOnTaskLaunch标签为true;

实现


整个任务、进程管理的核心实现,尽在ActivityManagerService中。上一篇说到,Intent解析,就是这个ActivityManagerService来负责的,其实,它是一个很名不副实的类,因为虽然名为Activity的Manager Service,但它管辖的范围,不只是Activity,还有其他三类组件,和它们所在的进程。
在ActivityManagerService中,有两类数据结构最为醒目,一个是ArrayList,另一个是HashMap。ActivityManagerService有大量的ArrayList,每一个组件,会有多个ArrayList来分状态存放。调度工作,往往就是从一个ArrayList里面拿出来,找个方法调一调,然后扔到另一个ArrayList里面去,当这个组件没对应的ArrayList放着的时候,说明它离死不远了。HashMap,是因为有组件是需要用名字或Intent信息做定位的,比如Content Provider,它的查找,都是依据Uri,有了HashMap,一切都顺理成章了。
ActivityManagerService用一些名曰xxxRecord的数据结构,来表达各个存活的组件。于是就有了,HistoryRecord(保存Activity信息的,之所以叫History,是相对Task栈而言的...),ServiceRecord,BroadcastRecord,ContentProviderRecord,TaskRecord,ProcessRecord,等等。
值得注意的,是TaskRecord,我们一直再说,Task栈这样的概念,其实,真实的底层,并不会在TaskRecord中,维系一个Activity的栈。在ActivityManagerService中,各个任务的Activity,都以HistoryRecord的形式,集中存放在一个ArrayList中,每个HistoryRecord,会存放它所在TaskRecord的引用。当有一个Activity,执行完成,从概念上的Task栈中退出,Android是通过从当前HistoryRecord位置往前扫描同一个TaskRecord的HistoryRecord来完成的。这个设计,使得上层很多看上去逻辑很复杂的Task体系,在实现变得很统一而简明,值得称道。
ProcessRecord,是整个进程托管实现的核心,它存放有运行在这个进程上,所有组件的信息,根据这些信息,系统有一整套的算法来决议如何处置这个进程,如果对回收算法感兴趣,可以从ActivityManagerService的trimApplications函数入手来看。
对于开发者来说,去了解这部分实现,主要是可以帮助理解整个进程和任务的概念,如果觉得这块理解的清晰了,就不用去碰ActivityManagerService这个庞然大物了。



参考:http://www.cnblogs.com/duguguiyu/archive/2010/02/22/1671547.html

http://winuxxan.blog.51cto.com/2779763/504047

http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html

http://blog.youkuaiyun.com/iefreer/article/details/4460196

http://winuxxan.blog.51cto.com/2779763/504835

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值