Application
一个典型Application由一个或多个相关的、松耦合的、用户可以与之互动的Activity组成。典型的情况,是一个Application打包进一个单独的apk文件里。Android会伴随着一大票的应用,可能包括电子邮件、日历、浏览器、地图、 短信、联系人、拍照、拨号、音乐播放、设置等等。
Android主屏一般就是应用启动者。一般来说,是一个有很多应用图标的滑动抽屉(就是默认Android系统下面那个,用手指往上一拖就出现很多图标的那个东西。),用户可以从上面启动应用。
Activity
当你建立Apllication的时候,你可以自己建立的Activity或者重用其它Application的Activity,来组装Application。 这些Activity是在运行时绑定在一起的,所以,新安装的Application能从已安装的Activity中获益。 一旦组合在一起,这些Acitivity会像一个整体那样一起工作。 一个Activity拥有一个独立的可视UI,这个UI应该基于单独的、明确界定的意图。 例如看图、编辑、拨号打电话、拍照、搜索、发送数据、语音命令等等其他用户行为。 一个需要界面显示的Application至少要有一个Activity。
当使用一个Android设备时,用户会从一个界面跳到另一个界面,这种跳转应该是流畅的。 不应该让用户察觉那些底层的行为,比如Activity间或者Task间的切换。
一个Activity持有了一种特定种类的内容(数据),以及接受一系列相关的用户动作。 一般来说,每个Activity的生命周期,与其他的同一个Application或Task内的Activity是无关的。 每一个Activity独立地被运行,用户或者系统可以按需要start、run、pause、resume、stop或者restart这些Activity。 由于这种独立的特性,有很多种方法可以覆盖或者重用Activity。
Android提供的拨号程序就是一个Activity组合的例子。这个程序是由4个Activity组合成的: 拨号,联系人列表,联系人详情,和新建联系人。如下所示:
Activity栈
当用户在Application中,从一个Activity跳到另一个时,Android系统会保存一个用户访问Activity的线性导航历史。 这就是activity栈,也被称为返回栈。 一般来说,当用户运行一个新的Activity,这个Activity就会被加到Activity栈里。因此,当用户 按BACK键的时候,栈中的上一个Activity就会被展示出来。 用户可以一直按BACK键,直到返回到了主屏。把Activity加入到当前栈里的操作,与Activity是否启动了一个新Task无关。 返回操作可以使用户从当前Task回到上一个Task。
只有Activity可以加到Activity栈里去,其它的,包括View、Window、Menu或者Dialog都不行。 这就是说,假设,界面A跳到界面B,然后用户可以用BACK跳回界面A。这种情况下,A和B都要被实现成Activity。 这个规则有一个例外的情况。那就是除非你的应用 控制了BACK键并且自己管理界面导航。
Task
一个Task是用户可以完成一个特定目标的一组Activity。与Activity属于哪个Application无关。
Activity栈可以由一个或多个Task组成。
打断Task
Task的一个重要的特性就是,用户可以中断他当前正在做的事(他的任务),去进行另一个Task,然后可以返回到原来的那个Task去完成它。 这个特性的意图,就是用户可以同时运行多个任务,并且可以在这些任务间切换。
有两种主要的的方法离开一个Task,这两种情况中,应该让用户能够返回到他们离开的那个任务:1:用户被Notification打断用户决定开始另一个任务
当然,规则总是有例外。除了上面提到了两种方法,确实存在第三种方法开始一个新任务,即,在代码中startActivity的时候,定义它要开始一个新Task。 地图和浏览器两个应用就是这么做的。 例如,在电邮中点击一个地址,会在新Task调出地图Activity,在电邮中点击一个链接,会在新的Task中调出浏览器。 在这种情况下,BACK键会回到上一个Activity(另一个Task中的电邮Activity),因为它不是从主屏启动的。
例子
1:从主屏启动一个Activity
主屏是绝大部分Application启动的地方。(某些应用只能从其它应用进入。) 当用户点击应用管理器的一个图标(或者主屏上的一个快捷方式),这个应用的主Activity会被启动到前台,并拥有用户的焦点。 像下面图标展示的那样,用户进入主屏并点击电邮图标的行为,启动了电邮Application的消息列表Activity。 主屏Activity被置到后台,stop。当用户重新进入主屏时,restart。
2:按着Activity导航,通过BACK键和HOME键离开Activity
一个Activity是否保持它的状态,取决于用户离开它的方式 - 是通过HOME键还是BACK键。默认情况下,按BACK键会finish(destroy)当前Activity,然后显示上一个Activity给用户。 用户通过在主屏点击电邮图标启动了显示出电邮消息列表的电邮应用。 用户滚动列表(改变了初始状态)。 点击BACK键销毁了消息列表Activity,然后回到了上一个Activity,即是主屏。 用户重新启动电邮应用后,电邮应用展示出来的列表,是重新初始化,没有经过滚动的。用户用HOME键而不是BACK键来离开消息列表Activity的情况 - 消息列表Activity会stop然后移到后台,不会被销毁。从快捷方式重新启动电邮Application,会把后台的这个应用 置到前台(从stop变成running)。滚动状态会跟用户离开的时候一样。
特例:按home键后再返回,为保留离开前的状态。某些后台Activity回到前台后,会进到它的初始界面(而不是离开时的状态)。 联系人应用和看图应用就是这种Activity。 当用户从主屏进入联系人应用,然后选择一个联系人看详细资料。(然后HOME键)。 如果用户再次从主屏进入,就会回到联系人列表而不是离开时的详细资料。 这样设计联系人应用,是因为联系人列表是整个应用4个Tab的主进入点。
特例:按back键后保留离开前的状态。不是所有的Activity都会在按BACK的时候被销毁。 当用户使用音乐应用播放音乐,然后按BACK键时,应用重写了后退的行为,Activity没被销毁,而仅仅是变得不可见。(所以保存了状态)。 音乐继续播放,并出现一个Notification,让用户可以回到音乐Activity去控制音乐的播放。 注意,你可以写不可见就销毁的Activity,也可以写像音乐Activity一样仅仅会切到后台的Activity。
重用一个Activity 假设Activity A启动了另一个Application中的Activity B,就说Activity B被重用了。 在Activity A广播查找能力然后B有这个能力时,这种情况就会发生。
设计Application的时候,考虑如何能够重用其他Application中的Activity, 以及考虑自己的Activity如何被其它Application重用,是一个很好的思路。 如果你的Activity拥有一个和已存在的Activity一样的Intent Filter, 系统会提供给用户一个对这些Activity的选择界面。
有返回的重用例子:Contacts重用了Gallery来取图片。Contacts Activity有一块区域用来显示联系人的图片,而一般来说,所有的图片都保存在Gallery里。 于是Contacts可以重用Gallery Activity来取图片。 这是一个重用Gallery Activity的很好的例子。 下面的图表说明了这个流程(主要部分)。 流程是这样的:用户点击Contacts,然后点选一个联系人查看详细资料,按MENU键,编辑联系人,点击图片区域, 于是就会启动Gallery Activity。用户找到想要的图片后,裁剪并保存。保存这个动作,会导致图片被插入到联系人详情的图片区域中。Gallery会返回图片给启动自己的Contact Application。
无返回的重用例子:Gallery重用Messaging来分享图片。 Sharing is 分享图片是另一个很好的关于在不同的Application重用Activity的例子。 如下图所示,用户启动Gallery,选一个图,按MENU键,选Share,选Messaging。 这就会启动Messaging Activity,然后建立一个新的消息并把图片附在上面。 然后用户填写“收件人”、写个短消息然后发送。 用户的关注点现在在Messaging程序上,Messaging Activity的重用什么都没有返回给启动它的Gallery Activity。
两个例子实际上都说明了Task — 为了完成一个目标的一系列Activity。 每个例子都是用了来自不同的Application的Activity来完成任务。
代替一个Activity
Activity A在不同的Application里代替了Activity B。 这种情况一般发生在Activity A做某项工作比Activity B好的时候。 换句话讲,A的功能足够代替B。 与重用不同的是,重用的时候,A和B做的是不同的事情。
用户选了RingsExtended,然后新的Activity被启动,代替了原来的Android系统电话铃Activity。
多Task
上面说过,用户可以从一个Activity通过HOME切到主屏,然后进入另一个Activity,而前一个Activity不会被销毁。 下面演示了进入Map Application的情况。
State 1 - 用户进入ViewMap Activity,搜索一个地址。假设网络非常慢,应用需要异常长的时间来搜索。
State 2 - 用户想在等待的时候做一些其它事情,所以他按HOME键,离开Map,但是没有打断网络连接。现在Map在后台继续工作。
注意,你可以自己决定,在程序被挪到后台后,是继续运行还是停止。 (参考onStop(),Activity生命周期)。 对于从网络上下载数据这类Activity来说,建议还是让它们在后台的时候,继续工作。用户就可以多任务了。
State 3
State 4 - 用户按HOME键,然后点击Map,回到Map Activity,看到地图已经读取完了。
主屏的应用启动器,在两个不同的Task里,启动了ViewMap和DayView两个Activity。 这个时候,系统就是多任务的 — 运行了多个Task。
每个Application必须至少有一个进入点 — 好让系统或者用户进到Application的Activity里。 在主屏的ApplicationLauncher,每一个图标表示了一个进入点。 Application也可以从其他Application进入。 每个Activity都是一个Application的潜在进入点。
Intent
当用户要对某类数据做出行为时,例如,点击一个mailto:info@example.com链接, 他们实际上是初始化了一个Intent对象(一个Intent)。这个Intent会被解决成为一个特定的Commpnent。 (这里我们只说ActivityComponent)。 (翻译成人话:用户要做一件事情,实际上会初始化出来一个Intent,广播询问系统,然后最终导致一个特定的Activity被调用。) 进而,用户点击mailto:链接的结果,是一个Intent对象。系统会尝试用这个Intent来匹配一个Activity。 如果这个Intent明确地指名了一个Activity(一个明确Intent),系统会立即进入这个Activity来响应用户动作。 然而,如果这个Intent没有指名Activity(一个不明确Intent),系统会比较这个Intent和所有可用的Activity的IntentFilter 。 要是多个Activity都能处理这个动作和数据,系统会让用户选择一个。
下图展示的就是点击mailto:链接的例子。 如果设备上有两个处理Email的应用,当用户点击电邮地址的结果,是出现一个对话框,让用户在两个应用中间选一个。(Gmail和Email两个应用)。
注意,Intent对象指定了两件事:行为和数据:一个需要进行的行为。上面的例子中,看、编辑、拨号、裁剪都是行为。要进行这种行为的特定的数据。上面的例子中,联系人列表,特定联系人,电话号码,图列表,特定的图都是数据。任何一个在主屏上启动Activity的Intent都是明确的Intent。 灵位,一些Activity进入自己Application内部的Activity使用的也是明确Intent。
在Task间切换
例子:用户写了一个文本消息,然后附上一张照片。在做这些事儿的时候,用户看了一眼日历。 然后,用户回来,继续附照片和发送消息。
开始第一个Task。
主屏 > Messaging > NewMessage > MENU > Attach > Pictures 最后一步操作启动了Gallery来取照片。 注意到,Gallery是在另外一个Application中。(同一个task中可能包含来自不同application的activity)在用户取图之前,用户决定去看一眼日历,这就是另一个Task了。 但是当前界面没有一个直达Calendar的按钮,所以用户必须回到主屏去。
开始第二个Task。
切换到第一个Task。并完成它。 看完了日历,用户于是按HOME键 > Messging,返回去继续附加图片。 这个操作并没有使用户进入Messaging,而是进入了刚刚离开的Gallery(即使这两个并不是一个Application!)。 然后用户选择一个图片,加入到消息里去,发送消息,完成第一个Task。
设计建议
当你写的Activity不希望被重用的时候,不要写IntentFilter。使用准确的Intent来调用它。(使用显示intent,不使用隐式intent)。当你不想你的Activity被其它Application重用的时候,要确定这个Activity上没有定义任何的IntentFilter。 只能从应用管理器进入,或者只能从同一个Application进入的Activity,应该应用这种做法。 对于这类Activity,使用目标明确的Intent来进入。(而不是通过Intent向系统查询能力这种做法)。 这种情况,也没必要使用IntentFilter。 IntentFilter是发布给所有其它Application的。 所以,如果你用了IntentFilter,你实际上就是在给你的Activity加入了一个外部入口,有可能会造成无意识的安全漏洞。
“当你重用其它应用的Activity时,不要忘记处理没有Activity满足要求的情况。”参考网址
“考虑你的Activity如何才能被其它Application重用。”