Applications Activities Activity Stack and Tasks.docx
《Applications Activities Activity Stack and Tasks.docx》由会员分享,可在线阅读,更多相关《Applications Activities Activity Stack and Tasks.docx(16页珍藏版)》请在冰豆网上搜索。
![Applications Activities Activity Stack and Tasks.docx](https://file1.bdocx.com/fileroot1/2022-12/13/f74dc683-4bb8-4bbf-b275-0804f2438c31/f74dc683-4bb8-4bbf-b275-0804f2438c311.gif)
ApplicationsActivitiesActivityStackandTasks
Applications,Activities,ActivityStackandTasks
理解Android系统中的四个基本概念对你是很有帮助的,它们分别是:
✉Applications
✉Activities
✉ActivityStack
✉Tasks
Applications
一个Android应用程序其实就是由一个或者多个Activity组成。
它们被捆绑在一起并存放进.apk文件中,这就是Android应用程序。
Android中有着丰富的应用程序,比如邮件、日历、地图定位、文本消息、联系人、照相机、打电话、音乐播放器,系统设置等应用。
一般情况下,桌面上都会有Android应用程序快捷图标,用户可以选择某一个图标来启动应用程序。
Activities
Activity是Android应用程序的主要组成部分,activity可以是你自己创建的,当然,还可以是其它应用程序中的activity。
它们都是在运行时绑定上的,以便于应用程序扩展其自身的功能,它们一旦组合在一起,就会如同一个应用程序一样。
每个activity都有其独特的UI、明确的功能,诸如打电话、看照片、听音乐等。
任何一个应用程序都应至少有一个activity。
当使用Android手机时,用户在屏幕上一个接一个地滑动或是点击图标启动activity,完全不会注意到底层的行为——他们体验是无缝的。
activity后面有activity,task后面还会有task。
Activity可以处理特定的数据类型和接受一相关的动作。
每个activity都有其各自的生命周期,互不干扰;并且它们都可以被用户或者系统独立启动、运行、暂停、恢复运行、停止、重新开始。
正因为这个独立性,activity可以以不同的方式被其它的activity进行替换。
比如打电话的应用程序就包含了四个activity:
打电话、联系人列表、查看联系人、添加联系人,如下图:
打电话联系人列表查看联系人添加联系人
下面的应用程序同样也包含了很多activity:
✉邮件-查看文件夹、邮件列表、邮件,发送邮件和设置邮件账号。
✉日历-查看天、星期、月、议程,编辑事件、首选项。
✉照相机-运行照相机、查看图片列表、图片,编辑图片,运行录像机,查看录像列表和录像。
✉游戏-玩游戏和安装游戏。
✉地图-查看地图上的位置,查看朋友的位置以及他们的详细信息(朋友的位置、状态、照片)。
Activity是Android应用中最为突出的组件,其余组件分别为:
service、contentprovider、broadcastreceiver。
更多activities的详情,参见ApplicationComponents。
ActivityStack
用户之所以能够从一个activity转到下一个activity,是因为Android系统针对activity而设计了一个线性的导航历史以供用户追溯访问,这就是activity栈,也称为backstack。
当用户启动了一个新的activity,它就被添加进activity栈,以便按BACK键时能够返回到上一个activity。
然而,用户不能按BACK键就直接返回到桌面(除非activity的前一个是桌面才可以)。
activity栈里面存放的只能是activity,而视图、窗体、菜单和对话框则不能。
也就是说,如果你可以让用户从屏幕A跳转到屏幕B,当用户按BACK键时,他就应该会回到屏幕A,那屏幕A必须是一个activity。
有个例外情况就是,你的应用程序需要利用BACK键控制自身的导航,那就要自己重新设定BACK键的导航功能。
Tasks
任务则是一系列的activity集合,它能使用户完成既定的操作,而又不用去关心这些activity是哪个应用程序里面的,除明确指定一个新任务之外(参见“中断任务”小段),那么其他activity都属于当前任务的一部分。
再次注意的是,这些activity可是任意应用程序中的其中一个,也就是说不管它们所属的应用程序是否相同。
举个例子,用户打开了联系人的程序,任务随之也会启动,他选择了email地址准备发邮件,这时跳转到了emailactivity,之后他要添加附件,需要在画廊中挑选图片。
这里面,联系人,email,图片画廊都是不同的应用程序。
当一个activity启动时,任务也随之启动的话,那个activity就是根activity。
启动activity一般有这么几种方式,应用程序发射器、桌面快捷方式、最近任务切换器。
Android系统内部一旦有任务,那么按BACK键就可以回到上一个activity。
Activity栈可以是多个任务的组成部分。
下面是关于任务的例子,以供参考:
✉发送文本消息并含有附件
✉观看YouTube视频并以邮件的方式向其他人分享。
中断任务——任务中有一个重要特性就是能使用户中断当前正在做的操作(他们的任务)而去执行其它操作,当然他们也可以回到之前的任务上,也就是说支持同时运行多任务并且来回切换它们。
这里有两种情况来开始其它任务,并且都可以返回到原先的任务上。
✉打开通知:
用户接收到通知并打开查看它。
✉用户转去做其它操作:
用户在桌面启动。
当然,也有例外的。
除了刚才提到的两种方式,另外还有一种打开新任务的情况,就在其内部开启一个activity。
例如,在邮件中以新任务的方式打开地图activity或是打开一个浏览器activity,当按BACK键时就又会回到邮件activity中。
Activities和Tasks之旅
下面的例子阐述了应用程序的基本原则,主要有activities,activity栈,回退键,任务和意图;并展示了系统是如何响应用户请求的,例如用户开始了一个应用程序,用户不断的切换UI,程序内部就是利用在不同任务之间切换activities的。
下面的许多例子你都可以在Android手机上运行起来。
在桌面上开始一个Activity
桌面是启动应用程序的主要地方,比如在桌面上点击应用程序图标就能将其打开,用户第一眼看到的就是应用程序中的主activity。
如下图,所描述的是用户在桌面点击Email图标所发生的事情:
用BACK和HOME键进行导航
Activity保持或者丢掉其状态完全取决于用户是怎样离开这个activity的——使用HOME键还是BACK键。
默认情况下,按下BACK键来结束(销毁)当前activity并为用户显示上一个activity。
例如下图,用户在桌面上打开了邮件,当前activity显示着邮件列表。
用户将列表滚动条往下拉以便看到后面的邮件,这时用户按了BACK键,那么Android就会销毁这个邮件信息列表activity并返回之前的activity(桌面)。
然后用户又重新打开邮件,还是那个列表,但是滚动条又回到了起始位置上。
上面的例子中,当按下BACK键就返回到了桌面,由于那是用户在上一次看到的activity。
不过如果用户从其他activity里面跳转到邮件列表,然后按下BACK键则回到了先前的那个activity(这里只是说明一下BACK键的作用)。
相比之下,下面的图就是用HOME键离开邮件列表activity而不是BACK键,那么当前activity就呈stop状态并移置后台而不是销毁。
当再次打开邮件列表activity时状态保持不变。
其它情况:
有些应用程序则不是如上面所说的那样。
例如联系人和画廊,用户在桌面打开联系人后查看了某个联系人的资料,接着再次打开联系人时,就不会显示之前的activity了。
这是因为联系人的主activity有四个标签,是为了让用户能够看到全部的功能特性。
此外,也不是所有activity都是当按下BACK键之后销毁掉的。
例如用户开始播放音乐,接着按下BACK键,却不会影响音乐的播放。
即使它的activity不再可见,音乐应用程序依然会在状态栏上提示着用户。
注意:
你也可以让activity不再可见时停止掉或是继续在后台运行,但后者更适合像音乐这样的应用程序。
重用activity
有两个应用程序中,它们分别也有两个activity:
activityA和activityB。
A的部分功能需要调用B的已实现功能,那么B就叫被重用。
联系人重用画廊来获取图片—联系人activity中会有联系人的照片,但是照片一般存放在画廊里面,所以联系人要重用画廊的功能来获取图片,画廊activity就是重用的绝佳例子。
下面的图画出了重用的流程。
具体流程是这样的:
用户打开了联系人,查看某一个联系人的资料并想编辑他的照片,这时,打开了画廊activity,对图片进行设置并保存,那个联系人的图片也就相应的改变了。
注意画廊最终会返回给联系人一张图片。
下一个例子讲述一个activity的重用并且不返回任何结果。
同样需要注意下面的插图是说明通过activity或是activity栈来实现历史导航——用户可以通过每个activity用任何方式回到桌面。
当开始设计一个应用程序时,一个不错的想法就是怎样能够在重用其它应用程序中的activity或是你的activity怎样被其它应用程序重用。
如果用一样的intentfilter(已经存在了一个activity)再添加一个activity,那么系统会为用户显示出一个选择UI,供用户选择使用那个activity。
画廊重用短信来与其他人分享图片。
分享也是不同应用程序之前重用的好例子。
如下图所示,用户打开了画廊,从中挑选了一张图片并点击了共享菜单,选择“短信”。
这时,就打开了短信activity,在其里面写些文字和附加上那张图片之后发送出去。
用户现在在短信activity当中,如果想回到画廊activity,就按BACK键返回。
注意这里的短信activity并没有给画廊返回任何的东西。
这些例子都在阐述任务——一系列的activities都在实现同一个目标。
每一个例子中的activity都是从两个不同的应用程序中完成本职工作的。
替换activity
这个例子描述的是不用应用程序中的两个activity互相替换,activityA替换activityB。
这种一般发生在activityA比activityB的功能更为强大一些。
换句话来说,A和B妥妥得等价,当然就可以实现A替换B。
这个例子中的联系人应用程序重用了activity,A和B虽然是完全不同的activity,但是它们两个彼此形成了互补,使程序更加的强大。
在这里例子中,用户下载了一个手机铃声的activity,称之为“铃声扩展”。
用户这时进入到“设置>声音&显示>手机铃声”里面,系统会展示两个可用activity供用户选择。
此时弹出的对话有一个选项是让你设置“是否默认使用此activity”,选中它。
当用户选择“铃声扩”时,以后在加载的时候就替换了Android默认铃声的activity了。
多任务
如前所述,当一个activity启动后,用户还可以回到桌面启动第二个activity,第一个activity则不会被销毁还是继续运行着,我们换个例子来说明这一小节——地图应用程序。
✉状态1:
用户打开了地图应用程序并查询一个地址。
这时,用户该说了,网络太TMD慢了!
因为地图定位是需要一些时间的。
✉状态2:
用户准备做些其它事情,按下HOME键,不过这样做不会干扰地图应用程序,还是保持其加载地图的状态。
✉状态3:
地图activity现在是在后台运行着,桌面在来到了前台。
这时用户打开了日历activity,比如查看今天是星期几。
✉状态4:
用户回到桌面,重新打开Map,这时地图已经全部加载完毕了。
以上两个应用程序“地图”和“日历”是两个不同的任务,因此Android支持多任务模式。
两个入口点
相对于应用程序来说,必须至少要有一个入口点,也就是至少要有一个activity。
桌面上的图标就代表着每个应用程序的入口点,同样也可以在其它应用程序中启动,当然,它们的入口点都在其内部。
而电话应用程序就有两个入口点:
联系人和打电话。
用户进入到联系人里面选择了一个电话号码并拨打该电话。
如下图的图所示,用户打开联系人,也就是启动了联系人的activity,然后选择了一个电话号码随之进入了打电话的activity,最后拨打它。
一旦用户在应用程序里,它们就可以通过标签、菜单项、列表项、屏幕上的按钮或其他用户界面访问诸如新增联系人和编辑联系人。
意图(Intents)
用户点击一个mailto:
的连接时,这实际上就被看作是一个意图,发邮件的意图。
关于意图有三点要说明:
✉如果是显式意图,Android就会立即启动那个activity。
✉如果是隐式意图,Android先去intentfilter寻找合适的activity再启动。
✉如果有多个合适的意图,Android就会列出一个意图选择列表供用户选择。
下面就举用户发邮件的例子,此时用户的Android上有两个邮件应用程序,当他在页面点击了mailto:
链接的时候,Android会提示给他一个对话框,其中有两个可用的程序供其选择(Gmail和Email)。
下面列举一些常用的意图和其对应的activity:
✉查看联系人列表:
对应联系人列表查看activity
✉查看指定的联系人:
对应联系人查看activity
✉编辑指定的联系人:
对应联系人编辑activity
✉发邮件:
对应邮件activity
✉拨打电话:
对应电话拨打activity
✉查看图片列表:
对应图片列表查看activity
✉查看指定的图片:
对应图片查看activity
✉裁剪指定的图片:
对应图片裁剪activity
意图必须由两部分构成:
动作和数据。
✉动作:
由上面的意图列表中可得出,查看、编辑、打电话、裁剪
✉数据:
由上面的意图列表中可得出,联系人的列表、指定的联系人、电话号码、图片列表、指定的图片。
注意:
任何在桌面上启动的应用程序都是显式意图,目的是指定其内部特有的那个activity。
同理,应用程序也可以在内部以显式意图的方式启动自身的activity,外部activity都是访问不到它们的。
关于意图更多信息,参见IntentsandIntentFilters。
切换任务
下面的例子描述的是用户如何在两个任务之间进行切换。
1.开始第一个任务。
你想要发送一条短消息并附加一张图片。
你会这样操作:
桌面>短消息>新的短信息>菜单>附件>图片。
最后一步启动了画廊activity来选择一张照片。
注意画廊是另外的一个应用程序。
在选择照片之前,可以先去桌面打开日历,目的是为了开始第二个任务。
2.开始第二个任务。
你会这样操作:
桌面>查看日历。
从桌面上打开日历,就等于是开始一个新任务了。
3.切换到第一个任务并完成后面的操作。
查看完日历之后,继续回到先前的任务上:
桌面>短消息,此时进入的并不是短消息activity,而是画廊activity,也就是之前离开的activity。
然后你就可以选择图片并发送短消息出去了,也就完成了第一个任务。
设计小贴士
下面的提示和指导都是针对应用设计者和开发者而提出的。
使用显式意图来防止外部应用调用你的activity
如果你不想自己的activity被外部使用,就别在manifest.xml里面配置intent-filter。
这样的话,你的activity就只会在应用程序内部来启动了,同样也避免了安全漏洞。
反之,创建一个意图并指定明确要启动的组件,这就是显示意图,在这个例子中,就不需要intentfilters。
Intentfilters可以发布所有的应用程序,当你创建了一个intentfilter时,其它应用程序就可以访问到你的activity了,至于它们怎么用,你就不知道了,这意味着不经意间形成了安全隐患。
如果使用外部的activity,但却没有匹配上,该怎么办?
有这一种情况,你利用Intent去调用外部应用程序中的activity,但遗憾的是,那个应用程序并没有安装进手机里,因此我们需要妥善的处理这种情况。
(译者注:
官方提出了两个不太完善的解决方案,我们来看下:
)
1.在启动那个activity之前用intent先对其测试一下。
2.如果启动activity会失败的话,则捕获它的异常信息。
以上更多信息请参阅官方文档提供的博客文章:
CanIusethisIntent?
。
该博文中提供了一种比较好的解决方式,正如其提供的样例代码中的isIntentAvailable()方法,我们可以在初始化阶段调用它;如果该应用不存在的话,我们就给用户提示一条消息,告诉他某某应用不存在,请去GoogleMarket下载等友好信息。
如果要让意图决定显示哪个activity,那我们就使用startActivity()或是startActivityForResult()来启动activity。
思考:
以怎样的方式来启动activity
做为Android设计者或开发者,完全取决于用户如何启动你的应用程序,而应用程序则是由一系列的activity组成,用户会从Home或是其他应用程序中启动这些activity。
✉在桌面点icon来启动应用程序主activity
如果你的应用程序是独立运行的,它应该是用户在屏幕上触摸应用程序的icon或是任务选择器当中来启动(这个机制需要在manifest.xml中配置intentfilter,action为MAIN,category为LAUNCHER)。
✉在其它应用程序中启动你的activity
这种方式就意味着你的activity是可重用的,也就是隐式意图。
许多应用程序中的数据都需要共享给其它用户的,例如,email、文本消息、上传下载等。
还会有一种情况是这样,就是当用户选择了一个功能,正好有一个或多个activity符合用户的这种需求,就会向用户提供一个activity列表供其选择。
举一个具体的例子,Gallery(画廊),它能让用户查看并共享图片,这时用户选择了“共享”菜单,Android系统会在intentfilter中寻找适合该请求的activity,如果有多个,就会以列表的形式展现给用户,供其选择。
在这个例子中,intentfilter能找到Email、Gmail、Messaging、Picasa等。
当其它的activity启动了你的activity时,会根据需求给它们返回一个结果。
>启动一个activity并需要返回一个结果
官方称这种方式为closedloop,也就是说当启动一个activity之后,会返回一个结果回来。
再拿上面那个例子来说,当用户完成上传或者发送的过程之后,会将图片信息返回给Gallery。
这个例子中的上传过程所用到的activity就是由外部的Gallery启动的。
(这种方式需使用startActivityForResult())
>启动一个activity不需要返回结果
官方称这种方式为open-ended。
举个例子,在Email中可定位一个住址,那么应用程序便会启动地图activity来定位地址,完成之后不会再给Email返回任何的结果;此时用户可以按BACK键回到Email中来。
(这种方式需使用startActivity())
✉只从其他应用程序中启动activity-前面所说的例子,Gmail、消息、Picasa(在Gallery启动)都是activity,它们均从桌面上的icon来启动的,与之形成对比的是,像裁剪图片和添加附件则不是在桌面启动的,因为这些都不是独立运行的。
实际上,并非所有的应用程序都有icon以供启动,它们都算作是一种小小的应用而已,因为它们使用并不频繁,而且其启动点都嵌在已有的应用程度当中。
例如,Android手机里面的打电话程序,其内部有个铃声设置功能,它存在于Android手机里面的设置(Setting)菜单里面,你也可以使用同样的Intent开发出一个定制的铃声设置应用,这样,在用户需要改变铃声时,会向其展示出两个铃声设置应用,一个是Android内置的,另一个就是你开发出来的。
如下图:
铃声设置并不是经常使用,而且其定义的功能也很明确,所以也就不需要在桌面提供应用程序icon了。
✉不同的图标能够启动多个相同的应用程序-由于Android应用程序的运行代码均存在于.apk文件中,因此就把这个文件看作是一个应用程序。
我们甚至还可以让其内部存在两个主activity,也就是两个应用程序启动入口点。
Camera.apk(照相机)就是一个非常好的例子,它内部就含有两个独立的主activity,Camera和Camcorder(摄像机);它们均拥有自己的icon并且独立运行;在用户角度上来看,这就是两个应用程序。
它们都共享使用一个镜头、在Gallery里面保存图片等。
实现这样的功能其实很简单,只需将它们都关联到不同的任务上即可。
(每个activity都其各自的任务,每个任务都有不同的亲缘性(affinity)。
(这个例子的两个应用它们所在的两个包是"com.android.camera"和"com.android.videocamera",有兴趣的可以深入研究)。
联系人和拨号器也是同一个应用两个主activity的典型例子。
✉应用程序部件-我们也可以将应用程序以部件的形式嵌进桌面上或是其它应用程序中并能它们持续更新。
允许你的activity添加到当前任务中
如果你的activity是在外部应用程序已经启动的话,那么也允许把它们添加到当前的任务中来(或者是已存在的任务——它有自身的affinity),这样做的话能会使用户能够在其它任务和你的activity之间进行自由切换。
但不包括你的activity仅有一个实例的情况。
对于这种行为,你的activity应该有一个standard或singleTop的启动模式,而不是singleTask或singleInstance,这样,你的activity就会以多实例的模式来运行。
通知,应该能让用户更容易的返回上一个activity
利用后台运行的服务能够给用户发出他们感兴趣的事件消息。
下面举个例子,主要是以Calendar为主,这个例子含两部分,一个是以Email的形式发出即将来临的消息,另一个是当有新消息时就发出通知。
我们来模拟一个应用场景,当某个用户处于activityA中,这时获取到了activityB发出的通知,他打开了这个通知,也就是进入到了activityB,当用户按下BACK键,他应该回退到activityA。
下面的具体流程描述了当用户响应通知时,activity栈是怎样工作的:
1.首先,用户在Calendar中设置了一个开会通知,也就是创建了一个新的事件,并将已写进Email中的部分信息复制到该事务上。
2.其它用户选择桌面>Gmail。
3.他们打开Gmail,接收到来自Calendar发出的一个开会通知。
4.接着他们打开了那条通知,进入到Calendaractivity中,并查看会议的简要说明。
5.这时用户进入到其里面查看更为详细的内容(就是在第一步当中复制的信息)。
6.当用户完成查看的操作时,按下了BACK键。
他们回到了Gmail上,也就是打开通知的那个地方。
但上面的流程在默认情况下却不是这样的。
通常情况下主要有两种方式来发出通