【转】关于Activity和Task的设计思路和方法

loganjr 2011-09-14

Activity和Task是AndroidApplicationFramework架构中最基础的应用,开发者必须清楚它们的用法和一些开发技巧。本文用大量的篇幅并通过引用实例的方式一步步深入全面讲解它们的基础原理(underlyingprinciples)和架构(mechanisms),例如:Navigation、Multitasking、activityre-use、intents和activitystack等…大部分与其相关的应用模块。重点讲解开发过程中如何更准确的体现用户交互性的便捷和高效,同时也帮助分析Designers和Developers在开发期间所要面对的问题。

文中涉及到的实例有一部分是属于平台自带的application(例如:拨号程序等),另外也有Google产品线中的一些有代表性的应用(例如:GoogleMap等)。建议大家亲自利用Emulator或者Android-powereddevice测试实例中的效果,这样可以帮助更加清晰的理解一些模块的含义。(注意:可能会因为硬件对于某些功能无法提供支持,所以有一些实例可能无法在你的测试机中正常浏览)

首先需要清楚一些基础模块:

Applications

Acitivities

ActivityStack

Tasks

以上这四个模块对于理解这篇文章非常重要,下边就来逐一的简单介绍其具体的含义和用法(也可以通过其链接直接查看官方文档)。

Applications

任何一个AndroidApplication基本上是由一些Activities组成,当用户与应用程序交互时其所包含的部分Activities具有紧密的逻辑关系,或者各自独立处理不同的响应。这些Activities捆绑在一起成为了一个处理特定需求的Application,并且以“.apk”作为后缀名存在于文件系统中。Android平台默认下的应用程序例如:Email、Calendar、Browser、Maps、TextMessage、Contacts、Camera和Dialer等都是一个个独立的Apps。

Activities

上边已经提到Activities是构成Applications的主要组成部分,其实可以更为具体的理解为Application仅仅是一个抽象的标签,它将系统内一部分Activities关联在一起,协同完成用户的特定需求。安装Application的过程也可以简单理解为将其所包裹的Activities导入到当前的系统中,如果系统中已经存在了相同的Activities,那么将会自动将其关联,而不会重复安装相同的Activities,避免资源的浪费。Application卸载的过程也会检查当前所关联的Activities是否有被其它Application标签所关联,如果仅仅是提供当前的Application使用,那么将会彻底被移除,相反则不做任何操作。

用户与Application的交互行为大部分都是通过GUI来完成,在Android平台可以有两种方式定义GUI,其中可以利用XML来预置静态的GUI元素,或者在Activity类的内部动态定义GUI元素。这两种不同的方法都是由Activity作为驱动和响应用户交互事件的主体。当启动Application之后,至少需要一个包含有GUI信息的Activity实例被创建。

Activity的主体包括两个主要部分,其中一个是Content(data),另外一个是响应用户交互事件的行为。列举一个Dialer例子的截图,其中包括四个部分:Dialer主界面、通讯录、查看联系人信息和添加新联系人。

下面列举了更多比较有代表性的Applications和其所包含的Activities:

Email-activitiestoviewfolders,viewlistofmessages,viewamessage,composeamessage,andsetupanaccount

Calendar-activitiestoviewday,viewweek,viewmonth,viewagenda,editanevent,editpreferences,andviewanalert

Camera-activitiesforrunningthecamera,viewingthelistofpictures,viewingapicture,croppingapicture,runningthecamcorder,viewingthelistofmovies,andviewingamovie

Game-oneactivitytoplaythegame,typicallyanotherforsetup

Maps-oneactivitytoviewalocationonamap,asecondforlists(suchasturnlistorfriendlist),andathirdfordetails(friendlocation,status,photo)

Application基本上是由四个模块组成:Activity、Service、ContentProvider和BroadcastReceiver,其中Activity是实现应用的主体。

ActivityStack

操作应用程序时,有时需要调用多个Activities来完成需求,例如:发送邮件程序,首先是进入邮件主界面,然后启动一个新的Activity用于填写新邮件内容,同时可以调出联系人列表用于插入收件人信息等等。在这个操作过程中Android平台有一个专门用于管理Activities堆栈的机制,其可以方便的线性记录Activities实例,当完成某个操作时,可以通过这个导航功能返回之前的Activity(通过按操作台的“Back”)。

每次启动新的Activity都将被添加到ActivityStack。用户可以方便的返回上一个Activity直到HomeScreen,到达HomeScreen后,将无法再继续查看堆栈记录(俗话说:到头了-Androidres.com)。如果当前Task被中止(Interruptingthetask),返回到系统主界面后启动了其它操作,当希望返回到前一个Task继续执行时,只需要再次通过主界面的Applicationlauncher或者快捷方式启动这个Task的RootActivity便可返回其中止时的状态继续执行。

相对于Views、Windows、Menus和Dialogs而言,Activity是唯一可被记录在Historystack中的数据,所以当你所设计的应用程序需要用户由A界面进入到次一级界面B,当完成操作后需要再次返回A,那么必须考虑将A看作为Activity,否则将无法从历史堆栈中返回。

Tasks

在Android平台上可以将Task简单的理解为由多个Activities共同协作完成某一项应用,而不管Activities具体属于哪个Application。通过下边的图示可以更清晰的理解Applications、Tasks、Activities三者之间的关系(Androidres.com提供):

Activities可以被看作为是独立存在于系统资源中,而且是作为实现具体应用的主体,Task将一些Activity关联起来实现一个更复杂的应用,单独或者多个Tasks可以被定义为一个Application。

通常实现一个Task都会存在一个RootActivity,但并不是所有情况都如此,通过Applicationlauncher、Homescreen的快捷方式或者由“RecentTasks”(长时间按住Home键)最近使用过的Task记录中启动。当从一个Activity中启动另外一个Activity时,Back键将作用于返回前一个Activity,与此同时新开启的Activity将被添加到ActivityStack中。

这里有两个被表示为Task的例子:

发送带有附件的邮件

查看YouTube视频,并且通过Email的方式共享给其他联系人。

-InterruptingtheTask

这是Task一个非常重要的特性,用户可以实时中止当前为完成的Task,新开启一个不同的Task,当新Task完成操作后,依然可以返回当上一次中止的Task继续完成余下操作。这个特性大大方便了同时运行多个Tasks,并且可以方便的在他们之间切换。这里有两种方式可以从当前Task跳转为其它Task(应用这两种方式切换Task,都允许返回到Task最初中止前的状态)。

系统抛出一个Notification,当前Task会被终止,跳转为Notification的Task。

用户强制中止

当然,除了这两种方式以外,还有另外一个特殊情况,算作为第三种方式来启动一个新的Task:Activity本身被定义为一个Task。例如:Maps和Browser就是属于第三种情况的Application,通过邮件中的一个地址来启动MapsActivity作为一个新的Task,或者通过邮件中的链接启动Browser来启动一个新的Task。当处在这种情况下,Back按键被触发后,将返回到上一个Task(邮件),因为这些新的Tasks并不是通过HomeScreen中的Applicationlauncher或者快捷方式来启动。

了解Activities和Tasks的基本原理

请大家一定首先理解之前所提及的内容,如果对某些概念依然含混不清,请及时查阅更多资料(官方文档是最好的学习资料),否则无法快速理解接下来将要讲述的例子,甚至丧失阅读兴趣。

接下来,将通过一些有代表性的实例了解关于Applications、Activities、Activitiesstack、Tasks和Intent等一些模块的最基本原理。从各个角度分析系统对于用户在不同模式下操作的反应原理。

从Home启动一个Activity

绝大部分的Application都由此启动(也有一些Application是通过其它Application启动)。具体的方式有两种,其一是从系统的ApplicationLauncher启动,另一种是直接由HomeScreen的快捷方式。启动Application后,RootActivity会显示在当前窗口,并可直接供用户操作界面元素。官方给出了一个有关这个过程的图示,其实我感觉这个描述的还不够直观,凑合着用吧。大体的过程是由Home下启动EmailApplication,在这个应用程序中可以直接提供给用户操作的是ListMessagesActivity,HomeActivity切换为后台运行。

应用Back或Home键离开当前Activity的区别

应用Back或者Home都可以离开当前Activity(基于Application的RootActivity),Homeactivity重新切换到foreground,然而二者最根本的区别在于用户是否还需要保留当前Activity的state。

-Back:

将会终止(Destroy)当前正在运行的Activity,返回到之前的Activity(如果是RootActivity,那么将会直接返回到HomeActivity)。官方给出了一个相关过程的图示,当用户正在操作ListMessagesActivity时,下拉邮件列表(改变了Scrolling状态),通过Back键返回到HomeActivity之后,当再次通过EmailIcon启动ListMessagesActivity时,将会看到列表处在初始位置。通过这个演示可以了解到通过Back键离开当前Activity时,无法暂时保留住其State数据,当再次启动时相当于重新创建了一个实例。

-Home:

利用Home取代Back返回的方式,当前Activity将被切换到Background,而不是被Destroied。这样的好吃是可以暂时保留这个Activity的State信息,当再次通过Applicationlauncher或者快捷方式启动时,可以返回到最后离开的状态。对比在Back中引用的例子,当再次由Home返回到Activity时,将会看到最后一次操作所记录的Scroll状态,而不是默认的初始位置。

Exception(例外情况)

前边列举了两种典型的情况,同时还存在一些例外的情况,某些Activity从Background被“召唤”到foreground之后依然是相当于重新创建了新实例,其有区别于前边所论述的结果。即便是暂时保存在Background模式下(没有被Destroied),其State数据也将丢失。例如:Contacts和Gallery等。当用户启动了Contact应用程序,并点选某个条目查看详细信息,如果通过Home键返回后,再次重复启动Contact应用程序时,看到的并不是之前所打开的特定条目的详细信息,而是初始的默认界面。这个例子说明不是所有情况下通过Home键返回后都可以保存当前Activity的State信息。

另外一种是与Back键有关的特殊情况。前边提及到大部分的Activity通过Back键返回到HomeActivity时,其自身将被彻底销毁,默认情况下Activity响应Back按键的方法被定义了Destroy行为。但对于某些特别情况,开发者可以根据需求将相应Back按键事件的行为重新“override”,撤消默认的Destroy行为。音乐播放器是与其相关的一个典型应用,当用户在播放器的RootActivity中触发Back按键后,转为Background模式下继续播放当前的音乐,同时HomeActivity转为Foreground。

Activity的复用

在多个不同的Applications中,当遇到有相同目的应用时,会涉及到Activity的复用性问题,这在开发过程中是一个非常普遍的情况。复用性一直被众多开发机构强调为节约成本,优化资源的最有效的机制。对于移动应用平台更加看重资源的最优化利用,复用性的应用在Android平台上无处不在,通过两个比较基础的例子来具体的说明。

-Contacts利用Gallery获得图像资源

众所周知Contacts是手机中最常用的应用程序,主要用于存储当前用户的联系人信息,其中需要包含联系人的头像信息。在Android平台中的图像信息是由Gallery管理,所以Contacts必然需要复用GalleryActivity来获取相应的图像信息。

针对于Android或者其它平台开发应用程序都需要有良好的复用性意识,这个需要贯穿于项目的整个开发过程。包括如何利用当前系统的现有资源,或者考虑到将来可能会被其它应用程序用于完成特定的需求。当用户正在调用的Intentfilter不唯一时,系统将弹出一个供用户选择的对话框,这的确是一个完美的解决方法。

-利用Messaging扩展Gallery共享功能

用户通过Gallery查看当前系统中的图像资源,每次单独打开一幅图像资源都可以通过Menu->Share将当前的资源以附件形式插入新创建的Messaging中,并且以正常发送信息的方式将其共享给收件人。如果取消当前的共享行为,只需要通过Back按键返回到GalleryActivity。相比较前一个例子的区别在于,MessageActivity完成发送或者被取消操作,其不会返回任何信息。

以上两个例子分别讲解了利用一系列的Activities来完成某一项需求,并且它们都调用了外部的Application资源。

ReplacinganActivity

目前要介绍的内容是关于在不同的Applications中,有相同Intentfilter属性的Activities可相互间替换,这对于习惯Windows等操作系统的用户比较不容易理解。其实如果您足够细心,就可以发现之前的例子中有关于这里所提及情况。

通常遇到这种情况发生时,一般都是因为外部具有相同功能的ActivityA在处理问题的能力方面要优于当前Application中默认的操作行为ActivityB,系统会抛出一个可供选择的对话框,用户根据主观判断来选择最优的方式处理当前任务。通过一个比较容易理解的实例来说明整个过程,建议“动手能力强”的同学可以通过模拟器亲自尝试。

例如:用户在当前系统下加载了最新的PhoneRingtoneActivity,取名为RingsExtended。如果用户通过Setting->Sounds&Display->PhoneRingtone来设置当前的铃音属性时,将会弹出一个包含有系统默认的PhoneRingtoneActivity和最新加载的RingsExtended两种可供选择的操作应用,同时在对话框中还提供了一种可以直接启动系统默认的操作方式选项。如果用户选择了RingsExtended,那么其将会被载入当前的线程中替代原有的默认操作行为,可以根据下面的图示来增强理解。

多任务同时运行(Multitasking)

在之前的板块有专门提到关于Home和Back两种切换到HomeScreen的方法和它们之间的差异性,这个章节将会重点涉及到系统可以同时处理多个实时运行的任务。如果用户正处于某个ApplicationA开启状态时,通过Home按键切换回HomeActivity的同时保留了此前ApplicationA运行的状态信息,可以开启新程序的同时,也可以再次将ApplicationA切换回Foreground。

接下来通过一个有关Map应用的实例更加具体的了解其所涵盖的过程。

首先的起始阶段分为三个步骤,

第一步,由ApplicationLauncher启动Map应用程序,并且搜索一个具体的地理位置。假设当前的网络环境非常不理想,需要花费一定的时间Download地图数据。

第二步,当系统需要花费较长时间加载当前地图信息数据时,保持当前Activity的状态,返回HomeActivity启动其它的Applicaton,地图Activity切换到Background,而并不会中断加载进度(依然保持网络连接)。

注意:以上是Activity在默认条件下的反应行为,其切换为Background状态后直接触发onStop()事件,开发者可以重新定义其方法。例如:强制Activity在转为Background状态下,终止网络连接。

第三步,当前Mapactivity已经切换到Background状态下运行,HomeActivity切换到Foreground。这时用户启动Calenderactivity,其将自动转为Foreground状态,同时获得操作焦点。

将以上三个步骤用图示的方式表述:

最后,退出当前Calenderactivity返回到Home,再次通过Maps图标将其处在Background状态的实例切换到Foreground。

通过上边的例子看出用户通过ApplicationLauncher同时运行多个Tasks,代表系统具备多任务处理机制-Runningmultipletasks。

启动Application的两种不同方式

每个App都需要提供至少一个Entrypoint(翻译成“入口点”有点别扭,干脆保留原样)供用户或者系统调用其所关联的Activities,Applicationlauncher中的小图标就是每个单独App的EntryPoint。另外App也可以相互间通过Activity作为EntryPoint来启动,可以将App所包含的每个Activity看作为潜在的Entrypoint。

系统中的PhoneApplication同样具有两个EntryPoints:Contacts和Dialer。下边的图示中可以了解到用户通过Applicationlauncher启动ContactsActivity,选择其中某一个联系人之后,调用DialerActivity拨打其所提供的电话号码。

Intents

在现实世界中大家每时每刻都会与周围的环境发生互动,这个互动的过程首先要确定一种意识,例如:感觉到口渴,需要水分补充。这种意识会引导自己以习惯的方式解决口渴问题,采用的方式可以多种多样,吃冰淇淋、喝水、嚼树叶等。类似于口渴的意识形态被抽象为Intent,并将其看作是一种对象,这就是Android响应“意识”的方式。

在Android平台上,用户的操作行为是由各种不同的事件组成,系统会将每个事件都抽象为Intent对象,寻找解决这项需求的具体方法。抽象的Intent对象有两种形式,第一种是“明确”的Intent(ExplicitIntent),在初始化的时候已经为这个Intent关联了特定的Activity。第二种是“不明确”的Intent(ImplicitIntent),代表这个Intent没有明确关联Activity,当它被抛出后,系统在众多Activities中根据Intentfilter来寻找与其匹配的处理方法。如果存在多个结果,用户可以根据需要选择合适的处理方法。

引用一个具体的例子,单击一个mailto:[email protected]链接后,这个被抛出的Intent属于ImplicitIntent,系统抓取了解决这个Intent的结果,将所有的结果供用户选择(Gmail或者Email):

下边给出更多系统默认的Intent关联列表:

Viewthelistofcontacts-resolvestoacontactlistvieweractivity

Viewaparticularcontact-resolvestoacontactvieweractivity

Editaparticularcontact-resolvestoacontacteditoractivity

Sendtoaparticularemail-resolvestoanemailactivity

Dialaphonenumber-resolvestoaphonedialeractivity

Viewthelistofimages-resolvestoanimagelistvieweractivity

Viewaparticularimage-resolvestoanimagevieweractivity

Cropaparticularimage-resolvestoanimagecropperactivity

Intent对象包含两个元素:

1)Action:例如查看、编辑、拨打电话、查看图像资源等等。

2)Data:提供给某种行为的具体数据。加工果汁饮料,需要提供水果(黑心店除外)。

参照官网的解释:IntentClass和IntentFilters。

Tasks相互间切换

依然是应用实例来说明这个切换的过程。在这个例子中,用户编辑一个短消息,并且插入图像附件,但是在发送之前启动Calendar,随后切换回短消息编辑界面,最后发送信息。

1)启动第一个Task:MessagingApp,Home>Messaging>NewMessage>Menu>Attach>Picture。插入图片的步骤需要调用GalleryActivity,它是一个独立的外部程序。

接下来启动另外一个Task,由于没有直接从当前的Activity运行Calendar,所以需要切换到Home。

2)启动另外一个Application(Calendar):Home>Calendar

3)查看Calendar完成后,将Messaging由Background切换到Foreground模式,其中还包括了添加附件,并最终发送消息。

至此,对于Android平台中两个比较核心元素:Activities和Tasks的介绍基本告一段落,以后也许会有更多关于这方面的讨论,希望得到您的关注。另外,有些朋友或许已经看过官方的原文,而本站也再次有幸得到了您的通读,如果在某些概念或者论述内容上存在遗漏或者误解,那么真诚的希望能够获得指正和帮助。

http://blog.chinaunix.net/space.php?uid=20783399&do=blog&cuid=1966109

相关推荐