Android之Activity

大鹏一日同风起,扶摇直上九万里。这篇文章主要讲述Android之Activity相关的知识,希望能为你提供帮助。
一、【Activity】
一个Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务,例如拨号、拍照、发送email、看地图。每一个activity被给予一个窗口,在上面可以绘制用户接口。窗口通常充满屏幕,但也可以小于屏幕而浮于其它窗口之上。
一个应用程序通常由多个activities组成,他们通常是松耦合关系。通常,一个应用程序中的activity被指定为"main"activity,当第一次启动应用程序的时候呈现给用户的那个activity。每一个activity然后可以启动另一个activity为了完成不同的动作。每一次一个activity启动,前一个activity就停止了,但是系统保留activity在一个栈上(“back stack”)。当一个新activity启动,它被推送到栈顶,取得用户焦点。Back Stack符合简单“后进先出”原则,所以,当用户完成当前activity然后点击back按钮,它被弹出栈(并且被摧毁),然后之前的activity恢复。
当一个activity因新的activity启动而停止,它被通知这种状态转变通过activity的生命周期回调函数。有许多回调函数一个activity可能会收到,源于它自己的状态变化-无论系统创建它、停止它、恢复它、摧毁它-并且每个回调提供你完成适合这个状态的指定工作的机会。例如,当停止的时候,你的activity应该释放任何大的对象,例如网络数据库连接。当activity恢复,你可以重新获得必要的资源和恢复被中断的动作。这些状态转换都是activity的生命周期的部分。
【Creating an Activity】
创建一个activity,你必须创建一个Activity的子类(或者一个Activity的子类的子类)。在你的子类中,你需要实现系统回调的回调方法,当activity在它的生命周期的多种状态中转换的时候,例如当activity被创建、停止、恢复或摧毁。两个最重要的回调方法是:
onCreate()你必须实现这个方法。系统调用它当创建你的activity的时候。在你的实现中,你应该初始化你的activity的基本的组件。更重要的是,这里就是你必须调用setContentView()来定义activity用户接口而已的地方。onPause()系统调用这个方法当用户离开你的activity(虽然不总是意味着activity被摧毁)。这通常是你应该提交任何变化,那此将会超越user session而存在的(因为用户可能不再回来)。
有若干其它生命周期回调函数你应该使用为了提供一个流畅的用户体验,并表操作异常中断会引起你的activity被中断甚至被摧毁。
1、Implementing a user interface
一个activity的用户接口被一个层次化的视图提供--继承于View类的对象。每个View控制activity窗口中的一个特定矩形区域并且能响应用户交互。例如,一个view可能是个button,初始化动作当用户触摸它的时候。
android提供大量预定义的view,你可以使用来设计和组件你的布局。“Widgets”是一种给屏幕提供可视化(并且交互)元素的view,例如按钮、文件域、复选框或者仅仅是图像。“Layouts”是继承于ViewGroup的View,提供特殊的布局模型为它的子view,例如线程布局、格子布局或相关性布局。你可以子类化View和ViewGroup类(或者存在的子类)来创建自己的widget和而已并且应用它们到你的activity布局中。
最普通的方法是定义一个布局使用view加上XML布局文件保存在你的程序资源里。这样,你可以单独维护你的用户接口设计,而与定义activity行为的代码无关。你可以设置布局作为UI使用setContentView(),传递资源布局的资源ID。可是,你也可以创建新Views在你的activity代码,并且创建一个view层次通过插入新Views到ViewGroup,然后使用那个布局通过传递到根ViewGroup给setContentView()。
【Declaring the activity in the manifest】
你必须声明你的activity在manifest文件为了它可以被系统访问。要声明你的activity,打开你的manifest文件,添加一个< activity> 元素作为< application> 元素的子元素。例如:

Android之Activity

文章图片

【Using intent filters】
一个< activity> 元素也能指定多种intent filters--使用< inetent-filter> 元素--为了声明其它应用程序可以激活它。
当你创建一个新应用程序使用Android SDK工具,存根activity自动为你创建,包含一个intent filter,声明了activity响应"main"动作,并且应该被 放置 在"launcher"分类。Intent filter看起来像这个样子。
Android之Activity

文章图片

< action> 元素指定这是一个"main"入口点对这个应用程序。< category> 元素指定,这个activity应该被列入系统应用程序列表中(为了允许用户启动这个activity)。
如果你希望应用程序自包含,并且不希望别的应用程序激活它的activities,那么你不需要任何其它intent filters。只有一个activity应该有“main"动作和”launcher“分类,就像前面这个例子。你不希望被其它应用程序访问原Activities应该没有intent filters而且你能启动他们通过自己显示的intent。
【Android之Activity】可是,如果你希望你的activity响应影含的intents,从其它应用程序(和你自己的),那么你必须定义额外的intent filters为这个activity。每一种你希望响应的类型的intent,你必须包含< intent-filter> ,包含< action> 元素,可选的,一个< category> 元素并且/或一个< data> 元素。这些元素指定你的activity能响应的intent的类型。
【Starting an Activity】
你可以开启另一个activity通过startActivity(),传递一个Intent描述了你希望启动的Activity。Intent指定要么准备的activity你希望启动或描述你希望完成的动作(操作系统选择合适的activity为你,可能来自定不同的应用程序)。一个intent可以传输小量数据被启动的activity使用。
完全工作在你的应用程序之内,你将经常需要简单的启动一个未知的activity。你可以这么通过创建一个intent显示的定义你希望启动的activity,使用类名。例如,下面显示一个activity怎么启动另一个activity命名为SignInActivity:
Android之Activity

文章图片

可是,你的应用程序或许希望执行一些动作,例如发送一份邮件、文件消息或者状态更新,使用你的activity的数据。在这种情况下,你的应用程序或许没有它自己的activity来完成这个动作,因此你可以促使设备上其它应用程序提供的activity来完成你的动作。这才是intent真正有价值的地方--你可以创建一个intent描述一个你希望执行的动作,然后系统启动一个合适的activity从其它应用程序。如果有多种activities可以处理这个intent,那么 用户可以选择哪一个来执行。例如,如果你希望允许用户发送邮件,你可以创建下面的Intent:
Android之Activity

文章图片

EXTRA_EMAIL额外的添加给intent一个字符串数组指定email地址,当一个邮件应用程序响应这个intent的时候,它读取这些字符串数组并且放置他们到相应字段。在这种情况下,email应用程序的activity启动并且当用户执行完,你的activity恢复。
【Starting an activity for a result】
有时,你或许希望接收一个结果从你启动的activity。在这种情况下,开启这个activity通过startActivityForResult()(而不是startActivity())。然后从随后的activity接收结果,实现onActiviryResult()回调函数。当随后的activity完成,它返回一个结果给你的onActivityResult()函数通过一个intent。
例如,或许你希望用户选择他们中的一个联系人,所以你的activity可以对这个联系人做些事情。下面是你怎么建立这样一个Intent和操作结果:
Android之Activity

文章图片

这个例子展现了基本的逻辑你应该使用的在你的onActivityResult()函数中,为了操作一个activity的结果。第一个条件检测是否请求成功--如果是,那么 resultCode将会是RESULT_OK--并且是否这个请求是否是这个响应是响知道--在这种情况下,requestCode匹配第二个参数用startActivityForResult()的参数。在那里,代码操作activity结果通过查询返回在intent中的数据(data参数)。
将发生的是,一个ContentResolver实现查询content provider,返回一个Cursor允许读查询的数据。
【Shut Down an Activity】
你可以关闭一个activity通过调用自身的finish()方法。你也可以关闭一个独立的activity你之前启动的通过finiActivity()。
注意:在大多数情况下,你不应该显示结果一个activity使用这些方法。正在下文所讨论的关于activity的生命周期,Android系统管理一个activity的生命周期为你,所以你不需要结果你自己的activity。调用这些函数对用户体验有害并且只有在你决对不希望用户返回到这个activity的情况下。
二、当一个activity转换到或转换出上面提到的状态,它被通过各种不同的回调函数通知。所有这些回调函数都是hook,你可以覆盖来做合适的工作当你的activity状态变化。下面的activity骨架包含每一个基本的生命周期函数:
Android之Activity

文章图片
注意:你的这些生命周期函数的实现必须调用父类的实现在你做自己的工作之前,就像上面显示的一样。
放在一起,这些函数定义了整个生命周期关于一个activity。通过实现这些方法,你可以监视三种嵌套的循环在activity生命周期中。1、一个activity的整个生命时间发生在onCreate()和onDestroy()函数。你的activity应该设置全局状态(例如定义布局)在onCreate(),并且释放所有的资源在onDestroy()。例如,如果你的activity有一个线程在后台跑下载数据从网络,它可能创建那个线程在onCreate()然后停止线程在onDestroy()。2、一个可视的生命期发生在onStart()和onStop()之间。在这期间,用户可以看见activity在屏幕上并且和它交互。例如,onStop()被调时当一个新的activity开始并且这个不再被可见。在这两个函数间,你可以维持资源那些需要用来展现activity给用户的。例如,你可以注册一个BroadcastReceiver在onStart(),然后注销在onStop()当用户不再看见你显示的东西。系统可能会调onStart(0和onStop()多次在这整个生命线期间,当activity在是否可见间转换的时候。3、一个activity的前景生命期发生在onResume()和onPause()之间。在这期间,此activity在所有其它activity之上在屏幕上,并且拥有用户焦点。一个activity可以经常转换进和转换出前景--例如,onPause被调用当设备准备休眠蔌当一个对话框产生。因为这种状态可以经常转换,代码在这两个状态应该轻量为了避免减慢转换速度使得用户等待。
图像1陈述这些循环并且展现一个activity可以发生的状态转换路径。矩形代表你能实现的回调函数。
Android之Activity

文章图片


Figure 1.  The activity lifecycle.
同样的生命周期回调函数被列在table 1,更加详细的描述了回调函数放置每一个在activity的事个生命周期,包含是否系统能杀死activity在回调函数完成后。
Table 1.  A summary of the activity lifecycle\'s callback methods. 
Android之Activity

文章图片


因为onPause()他们三个中的第一个,一量activity被建立,onPause()是最后一个被保证调用的方法--如果系统必须恢复内存在紧急状态,然后onStop()和onDestroy()可能将不被调用。因此,你应该使用onPause()来写数据(例如edit)的存储。可是,你应该精心挑选哪个信息一定要在onPause()中留住,因为任何何阻塞的操作在这个函数阻塞到下一个activity的转换因而降低用户体验。
【Saving activity state】本文简单的提到了什么一个activity被paused和stopped,activity的状态维持着。这是真的因为activity对象仍然在内存当它被paused或stopped--所有的信息关于它的成员变量和当前状态都存在。如此,任何用户的变化在activity中都维持在内存里,所以当activity返回到前景的时候(resumes),这些变化仍然存在。
可是,当系统摧毁一个activity为了恢复内存,activity对象被摧毁了,所以系统不能简单的以完整状态resume它。代替的是,系统必须创建Activity对象如果用户浏览回它。也就是,用户不知道系统摧毁了activity并且再创建了它,如此,可以希望activity是它刚才的那个。在这种情况下,你可以确定重要的信息关于activity的状态被保留通过实现一个额外的回调函数,允许你保存信息关于你的activity,然后恢复它当系统重新创建它的时候。这个你可以保存当前状态信息的回调函数是onSaveInstanceState()。系统调这个函数在摧毁activity这前,并且传递一个Bundle对象。Bundle就是你可存在状态信息的地方,采用name-value对,使用方法如putString()。然后,如果系统杀死你的activity进程并且用户浏览回到你的activity,系统传递Bundle给onCreate,这样你就可以恢复activity状态你在onSaveInnstanceState()时候保存的状态。如果没有信息被保存,传递给onCreate()的Bundle是null。注意:不保证onSaveInstanceState()将会被调用在你的activity被摧毁前,因为有不需要存储的情况存在(例如当用户离开你的activity,用户显示的结束)。如果这个方法被调用,它总是在onStop()前,概率性在onPause()前。
可是,即使你不做什么事也不实现 onSaveInstanceState(),一些activity的状态被activity类的默认onSaveInstanceState实现恢复。特别的,默认的View的onSaveInstance()在布局方面,允许每一个视图提供它自己的信息来被保存。几乎所有 的Android框架中的widget实现这个方法合适的,这样任何可见的变化对UI都自己保存并且恢复当你的activity被创建的时候。例如,EditText widget保存任何文本被用户输入的,并且CheckBox widget保存是否选中还是没选中。惟一的工作要求你的是提供一个惟一的ID(通过android:id属性)给每一个希望保存状态的widget。如果一个widget没有ID,那么 它不保存它的状态。虽然onSaveInstanceState()的默认实现保存非常有用的信息关于你的activity UI你仍然会需要覆盖它来保存额外的信息。例如,你或许需要保存成员变量在activity生命期间变化的(和保存在UI中的数据有关,但保存这些UI值的变量却不会被默认保存)。
因为默认的onSaveInstanceState()帮你 你保存UI状态,如果你覆盖这些方法为了保存额外的信息,你应该每次调用父类的实现关于onSaveinstanState()在做工作之前。注意:因为onSaveInstanceState()不保证被调用,你应该用它仅用它记录临时状态关于你的activity--你应该永远不用它来存储永久性数据。代替的是,你应该用onPause()来存储永远性数据(例如保存在数据库中的数据)当用户离开activity。一个好的方法测试你程序保存状态的功能是简单的转换设备以使屏幕方向改变。当屏幕方向变化,系统摧毁和重建activity为了应用可选的资源,适合新方向的。出于这个原因,你的activity恢复它的状态当创建的时候非常重要,因为用户通常转换屏幕在使用应用程序的时候。
Android之Activity

文章图片

【Handling configuratinon changes】
一些设备配置能在运行时改变(例如屏幕方向,键盘是否可用和语言)。当这样的变化发生时,Android重新开启运行中的Activity(onDestroy()被调用,然后onCreate()被调用)。重启行为被设计来帮助应用程序适应新的配置通过自动加载你的应用程序通过可选的资源,你提供的。如果你设置你的activity合适的操作这个事件,它将更有弹性对生命周期中无法预料的事件。最好的方法操作配置变化,例如屏幕方向变,就是简单的预定义你应用程序的状态使用onSaveInstanceState()和onRestoreInstanceState()(或onCreate()),就像前面讨论的。
【Coordinating activities】
当一个activity启动另一个,他们都在体验生命周期转换。第一个activitypauses和stop(虽然,它不会stop如果他仍然可见在前景),另一个activity被创建。万一这些activities共离数据保存到磁盘或哪,那就让第一个activity不退出在另一个启动前就显得非常重要。相当的情况是,后一个进程启动覆盖了前一个停止的时候。
生周周期回调被良好的定义了,特别的当两个activities都在同一个进程,并且其中一个启动另一个。下面是操作顺序当activity A启动activity B:1、Activity A 的onPause()的方法执行。2、Activity B 的onCreate()、onStart()和onResume()方法执行,顺序的。(B现在拥有用户焦点)3、如果A不再可见,它的onStop()方法执行。
这种生命期函数调用序列允许你从一个activity到另一个的信息转换。例如,如果你必须写入一个数据库,当第一个activity stop,以便让下一个activity能读它,那么你应该写入数据库在onPause()而不是onStop()。
 

    推荐阅读