Android帮助文档翻译——开发指南(二)Activity

转载时请注明转自:http://blog.csdn.net/sam_zhang1984

交流可加新浪微博:Android开发人

 

Activity (这个觉得没必要翻译,直接用还好理解 @ Sam )就提供与用户交互的界面,用户通过这个交互界面可以进行一些操作,比如打电话、照相、发邮件或查看地图。每一个 Activity 都提供了一个可以绘制用户交互界面的窗体,这个窗体通知充满整个屏幕,但也可以是小于屏幕或悬浮于其它窗体上。

一个应用程序通常是由多个松耦合的 Activity 组成。一般情况下,在一个应用程序的多个 Activity 中有一个 Activity 会被指定成主 Activity main ),这样当程序第一次运行时用户就会首先看到这个 Activity 的界面。程序运行后,每一个 Activity 都可以启动其它 Activity 来实现用户的操作,当启动了一个新的 Activity 后,旧的 Activity 将会停止,而新的 Activity 会被系统压到一个栈中(称为“ back stack ”,这个名词也不准备翻译 @ Sam )。即当有一个新的 Activity 启动时,系统会把这个 Activity 压入到 back stack 中并获取用户的焦点。这个 back stack 遵循“后进先出”的队列原则,因此,当用户操作完当前 Activity 并按了“返回”值后,当前这个 Activity 将会从栈中被弹出并销毁,之前的 Activity 将重新恢复。

当一个 Activity 由于有新的 Activity 被启动而停止时,它将会通过 Activity 的生命周期回调方法获取到这个状态改变的信息。一个 Activity 可能会因为状态的改变而收到多个回调方法,无论是系统创建了这个 Activity 、停止这个 Activity 、恢复这个 Activity ,还是销毁这个 Activity ,都会接收到一个回调方法。利用这些回调方法,你可以在 Activity 状态改变时进行一些相应的操作工作,比如当 Activity 在停止时,你可以释放像网络连接和数据库连接这样的一些重量级的对象;当 Activity 恢复时,你可以重新申请必要的资源和恢复一些中断的动作。这些状态之间的转换就构成了一个 Activity 生命周期。

本节接下来主要讨论如何创建和使用一个 Activity ,包括详细讨论一个 Activity 的生命周期,这样你就可以在这些状态转变过程中进行一些合适的操作。

 

创建一个 Activity

要创建一个 Activity 就必须创建一个继承了 Activity Activity 子类的类。在你所创建的类中,你可以实现其回调方法,让系统在 Activity 状态(创建、停止、恢复或销毁)改变时调用。其中最重要的两个回调方法是:

onCreate()

这是一个必须实现的回调方法。它是在 Activity 创建时调用的。在你实现的这个回调方法中,你应当初始化你这个 Activity 里面所必需的组件,更重要一点是你必须调用 setContentView() 方法定义你这个 Activity 用户交互界面的布局。

onPause()

这个方法是当用户离开 Activity 时系统调用的第一个方法,所谓用户离开 Activity 不一定是说 Activity 被销毁。由于此时用户还没返回到这个 Activity 因此在这个回调方法中你一般需要提交一些用户当前会话中需要保存的数据

除了这两个回调方法外,还有一些其它的生命周期回调方法可以使用。你可以使用这些回调方法让用户在各种 Activity 切换过程有更好的用户体验,也可以处理一些会引起 Activity 停止甚至销毁的意外中断。所有的生命周期回调方法会在后面再进行讨论。

 

实现一个用户交互界面

Activity 的用户交互界面由一系列从 View 类派生出的视图( view )体系组成的。每一个视图( view )控制了 Activity 窗体中某一个特定的矩形区域,并能对用户交互动作进行响应。比如,一个视图( view )可能就是一个已经创建响应用户触摸动作的按钮。

Android 提供了很现成的视图,你可以直接使用这些来设计和组织你的布局。对于能提供在屏幕上可视且可交互元素的视图我们称其为“ Widgets ”,比如按钮、文字标签、复选按钮或一张图片。而那些由 ViewGroup 派生并能为其子视图提供唯一布局模式的视图我们称其为“ Layouts ”,比如线性布局、网格布局或相对布局。你也可以自己通过继承 View ViewGroup 或其子类,创建自己的 Widgets Layouts ,并在你的 Activity 布局中使用它们。

使用视图定义一个布局的最普遍做法,是在你的应用程序资源中使用一个 XML 布局文件进行定义。通过这种做法,你可以脱离定义了 Activity 行为的源代码来维护你的用户交互界面的设计。你可以调用 setContentView() 方法并传递进一个布局的资源 ID 来为你的用户交互界面( UI 下面都直接使用 UI 代替 @ Sam )。尽管如此,你也可以代码中通过在一个 ViewGroup 中插入新视图来创建一个视图体系,这时你就需要传根 ViewGroup setContentView() 方法来设置你的 UI 布局。

 

manifest 文件中定义 Activity

为了让系统能使用你的 Activity ,你必须在 manifest 文件中定义它,定义的方法是:打开 manifest 文件并在 <application> 标签下增加一个 <activity> 子标签,代码如下:

   

在这个 <activity> 标签中,你还可以添加更多的属性来定义 Activity 的性能,如 Activity 的标签、图标或 UI 界面样式的主题。更多关于 <activity> 标签的属性描述见 reference 章节。

使用意图过滤器

一个 <activity> 标签也可以使用一个 <intent-filter> 子标签来指定意图过滤器,这样其它应用程序的组件就可以通过这个来激活它。

当你使用 SDK 工具新建一个应用程序时,开发工具会自动为你的首个 activity 加入一个可以响应“ main ”动作并放在程序列表 "launcher" category )意图过滤器,代码如下:

   

<action> 标签指定了应用程序的“ main ”入口, <category> 标签则表明了这个 activity 应该放设备的程序列表中并允许用户使用。

如果你希望你的程序是独立并且不允许其它应用程序调用,那你就可以不加任何意图过滤器( intent filters )。就像上面的代码例子一样,一个 activity 只能有一个“ main ”动作和一个 "launcher" category (这个真不知怎么翻译 @ Sam )。那些你不想要其它应用程序调用的 activity 就不要添加意图过滤器,但你想在自己应用程序中调用的话就必须使用显式的调用(后面章节会详细阐述)。

然而,如果你希望你的 activity 要对其它应用程序(或你自己的程序)的隐式意图进行响应,那么你就必须为你的 activity 添加一个意图过滤器。你必须为每一种准备进行响应的意图类型用 <intent-filter> 标签和 <action> 子标签进行定义,还可以添加 <category> 子标签和 <data> 子标签进行辅助描述。通过这些标签的定义,你就可以指定你的 activity 能响应的意图类型。

更多关于 activity 如何响应意图的讲解,可以参考“意图和意图过滤器”一节。

 

启动一个 activity

通过调用 startActivity() 方法,并传递进一个描述你想要启动activity 的意图,你就可以启动其它 activity 了。被传递的这个意图即可以明显指定你要启动的 activity ,也可以描述你想执行的动作。通过后者的方式,系统会为你选择一个合适的 activity 来执行,当然是包括不同应用中的 activity 。一个意图同时可以携带一些目标 activity 需要的轻量级数据。

当在你自己的应用程序中工作时,你经常需要启动一个已知的 activity 。启动方法就用一个包含有类名明的意图确指定你想启动的 activity ,然后传递过去就行。下面例子就演示了如何启动一个名叫 SignInActivity activity

   

尽管如些,你有时可能需要用你自己的数据然后在其它 activity 中执行某一个动作,比如发邮件、发短信或更新状态等。在这种情况下,有可能你自己的应用程序中没有可以执行这个动作的 activity ,那么你就可以使用另一种代替的方法来完成这个操作:让设备上其它应用程序中能为你执行这个动作的 activity 为你执行。这就是意图的真正价值所在,你可以创建一个描述你要执行动作的意图,然后系统就会为你激发一个其它程序中合适的 activity 。如果设备上有多个 activity 能胜任这个动作,那么就由用户自己去选择是用哪一个去执行。以下给出创建一个能让用户发邮件的意图:

   

上面的 EXTRA_EMAIL 附加字符串数据就是送邮件时收件人的地址( EXTRA_EMAIL 是一个键,真正的值即收件人地址是后面 recipientArray @ Sam )。当一个 Email 程序对这个意图进行响应时,它会把这个附加字符串放到新建邮件的收信人一栏中。这时 Email 程序的 activity 将被启动,当你完成了发邮件操作后返回时,你的 activity 就又恢复了!

 

启动一个带返回值的 activity

有时你可能需要从你所启动的 activity 中返回数据,这种情况下,启动 activity 时就需要调用 startActivityForResult() 而不是 startActivity() 。通过实现 onActivityResult() 回调方法,就可以接收被启动 activity 的返回值。当被启动 activity 执行完成后,它将返回一个意图( Intent )给 onActivityResult() 方法。

举个例子,你或许需要用户去选取一个联系人,然后你的 activity 就可以对这个联系人信息进行相关处理。下面就给出了如何去创建这样一个意图并其返回值的代码:

   

这个例子为我们展现了用 onActivityResult() 方法处理 activity 返回值的基本逻辑模型。首先,检测请求是否成功(即 resultCode 是否为 RESULT_OK );其次,检测是否是当前请求是否是已知的(即 requestCode 是否与 startActivityForResult() 的第二个参数匹配);最后,代码中通过查询一个 Intent 类型数据(即第三个参数 data )来处理 activity 返回值。

其中有一个 ContentResolver 执行了一个针对内容提供器( content provider )的查询,并返回一个允许去读限实际数据的游标( Cursor )。

 

关闭一个 Activity

你可以通过调用 finish() 方法来关闭一个 activity ,也可以通过调用 finishActivity() 来关闭另一个你之前启动过的 activity

注:在大多数情况下,你不需要使用这个方法显式地去关闭一个 activity 。正如接下来关于 activity 生命周期中会讨论到一样, Android 系统会为你管理 activity 的整个生命周期,因此你不需要亲自去关闭一个 activity 。调用这个方法可能会对用户体验起到一些负面影响,只要当你确信用户没必要再返回到这个 activity 实例时才亲自去关闭它。

 

管理一个 Activity 的生命周期

通过实现回调方法来管理 activity 的生命周期是开发一个健壮灵活应用程序的关键所在。与其它 activity 、它自己的任务( task )和 back stack 的联系直接影响了一个 activity 的生命周期。

一个 activity 基本存在三种状态:

1、             恢复:当一个 activity 处于屏幕前台并取得用户焦点时所处的状态,通常这种状态也被称为正在运行

2、             暂停:另一个 activity 前台并取得用户焦点,但这个 activity 仍然是可见时所处的状态。即另一个 activity 处于这个 activity 之上,但它是半透明或没覆盖整个屏幕。一个暂停的 activity 是完全存活着的,即它被保留在内存里面,保持所有状态和成员信息,并且它的残骸(实在不知怎么翻译 @ Sam )则交给窗体管理,但是在内存不足的情况下它仍然是可以被系统销毁的。

3、             停止:一个 activity 被另一个 activity 完全遮蔽,即处在后台时的状态。一个停止的 activity 仍然是存活着的,即这个 activity 对象仍保留在内存中,保持所有状态和成员信息,但它就不隶属窗体管理了 。但它对于用户来说是不可见的,并且在其它需要内存时就会被系统销毁。

如果一个 activity 处于暂停或停止状态,那么系统就可以通过调用 finish 方法关闭它或直接销毁它的进程来释放掉这个 activity 所占的内存。当已经从内存释放后,如果再次打开这个 activity ,那就得重新创建它了。

 

实现生命周期回调方法

当一个 activity 在上面讨论的各种状态间过渡时,系统会通过调用相应的回调方法通知它状态的改变。所有这些回调方法都是可以覆写的 hook ,我们可以通过覆写这些方法可以在 activity 改变状态时进行一些合适的操作。下面是一个包含了所有生命周期基本方法的 activity 模型:

   

注:正如上面例子显示一样,你实现的所有生命周期方法时,必须在你的实现实体之前调用父类的实现。

从整体看,这些方法定义了 activity 的整个生命周期。通过实现这些方法,你可以监视 activity 生命周期中以下的三个嵌套循环( nested loops ):

1、             activity 从调用 onCreate() 到调用 onDestroy() 过程中的整个生命周期。你的 activity 需要在 onCreate() 中设置一些全局的状态属性(例如定义布局),在 onDestroy() 中释放占用的所有内存。例如,如果你有一个用来下载网络数据的线程运行要运行在后台,就应该在 onCreate() 时创建它,并在 onDestroy() 时停止它。

2、             activity 从调用 onStart() 到调用 onStop() 过程中的可视生命周期。在这个时段时,用户可以在屏幕上看到 activity 与之交互。例如,当有一个新的 activity 启动并且当前 activity 不可见了后,将会调用 onStop() 。在这两个方法之间,你可以保持一些显示 activity 给用户看必要的资源。例如,你可以在 onStart() 方法中注册一个广播接收器来监听影响 UI 的一些改变,而在用户看不到你的程序所显示的内容时注销它,即调用 onStop() 时注销(因为当 activity 不可见时,更新 UI 已经没有意义了,即这个广播接收器已经没存在的意义,所以可注销它 @ Sam )。系统可能会 activity 的整个生命周期中多次调用 onStart() onStop() ,就好像 activity 对于用户来说总是在可见和不可见之间交替一样。

3、             activity 从调用 onResume() onPause() 之间的前台生命周期。在这个期间里面 activity 处于屏幕前方并取得用户焦点。一个 activity 可能会在前台和后台间频繁切换,例如,在设备休眠时或出现一个对话框时会调用 onPause() 。因为这个状态间切换非常频繁,所以在这两个方法中的代码最好是相对轻量级一点,这样才可以避免切换太慢,要让用户等待相对长时间。

1 描述了这个循环过程以及一个 activity 在各种状态之间切换的路径。矩形框表示你可以实现在 activity 切换状态过程中执行操作的回调方法。

 

                                                           图1


表格 1 同样是这些生命周期回调方法。不过这里描述得更为详细,明确其各自在一个 activity 整个生命周期的位置,同时说明了在执行某个回调方法后系统会不会释放掉 activity 的内存。

 

 

 

方法描述其后能否销毁

下一

方法

onCreate() activity 第一次创建时会被调用。在这个方法中你需要完成所有的正常静态设置 ,比如创建一个视图( view )、绑定列表的数据等等。如果能捕获到 activity 状态的话,这个方法传递进来的 Bundle 对象将存放了 activity 当前的状态。调用该方法后一般会调用 onStart() 方法。 onStart()
    onRestart()

activity 被停止后重新启动时会调用该方法。其后续会调用 onStart 方法。

onStart()
onStart()

activity 对于用户可见前即调用这个方法。如果 activity 回到前台则接着调用 onResume() ,如果 activity 隐藏则调用 onStop() (好像有点问题,怎么会是调用 onStop 呢?? @ Sam )。

onResume()
or
onStop()
    onResume()

activity 开始与用户交互前调用该方法。在这时该 activity 处于 activity 栈的顶部,并且接受用户的输入。其后续会调用 onPause() 方法。

onPause()
onPause() 在系统准备开始恢复其它 activity 时会调用该方法。这个方法中通常用来提交一些还没保存的更改到持久数据 中,停止一些动画或其它一些耗 CPU 的操作等等。无论在该方法里面进行任何操作,都需要较快速完成,因为如果它不返回的话,下一个 activity 将无法恢复出来。如果 activity 返回到前台将会调用 onResume() ,如果 activity 变得对用户不可见了将会调用 onStop() onResume()
or
onStop()
onStop()

activity 对用户不可见时将调用该方法。可能会因为当前 activity 正在被销毁,或另一个 activity (已经存在的 activity 或新的 activity )已经恢复了正准备覆盖它,而调用该方法。如果 activity 正准备返回与用户交互时后续会调用 onRestart ,如果 activity 正在被释放则会调用 onDestroy

onRestart()
or
onDestroy()
onDestroy()

activity 被销毁前会调用该方法。这是 activity 能接收到的最后一个调用。可能会因为有人调用了 finish 方法使得当前 activity 正在关闭,或系统为了保护内存临时释放这个 activity 的实例,而调用该方法。你可以用 isFinishing 方法来区分这两种不同的情况。

没有

 

“其后能否销毁”一列表示在该方法返回后,系统能否不执行 activity 任何代码直接销毁托管了当前 activity 的进程。其中有 onPause() onStop() onDestroy() 三个方法执行后是可以销毁的。由于 onPause() 是这三个方法中第一个会被执行的,因此它是在 activity 创建后销毁前这个过程中,最后一个能被确保执行的方法。如果由于突发事件系统需要回收内存, activity 将在执行 onPause() 后被销毁,这时 onStop() onDestroy() 就可能不会被调用了。因此,你需要在 onPause() 方法中保存一些关键的永久性数据,如用户在编辑的内容。尽管如此,你也必须有选择性地在 onPause() 方法中保存一些数据,因为在这个方法中的任何阻塞处理过程,都会阻塞住下一个 activity 的切换,在用户体验方面就会觉得程序很慢。

在“能否销毁”列中是“否”的那些方法,表示可以保证托管了当前 activity 的进程在调用该方法后不会被系统直接销毁。因此,一个 activity onPause() 方法返回后,调用 onResume() 方法前是可以被销毁的;在重新调用了 onPause 方法并返回前, activity 是不可以被销毁的。

注:一个 activity 不一定是表格中定义的技术上的“是否可销毁”,它有时也会被系统所销毁,但这种情况只有在系统中已经没有资源的极端情况下发生。

保存 activity 的状态

在“管理 activity 生命周期”一节中已经简略地提到了,可以在 activity 被暂停或停止时保存 activity 的状态。因为当 activity 对象被暂停或停止时,它仍然保留在内存里面,关于它的成员信息和当前状态都是活动的,所以在这个时候进行保存是有效的。从而使得用户在 activity 中所做的变更都会被保存到内存中,这样的话当 activity 又返回到前台时,所有的这些变更仍然保持不变。


尽管如此,当系统由于要回收内存而把 activity 销毁时(即当 activity 对象已经被销毁),系统就无法简单地恢复它的状态,而是在用户要返回这个 activity 时,重新创建一个 activity 。但用户是无法察觉到系统销毁和重建 activity 的这些操作,因此用户可能要求返回到的 activity 要与之前是一样的。这个情况下,你就需要实现另一个能保存 activity 状态信息的回调方法来保存 activity 的一些重要信息,并在系统创建它时把这些信息读取出来。

你可以用回调方法 onSaveInstanceState() 来保存 activity 的当前状态信息。系统会在 activity 将被销毁前调用这个方法,并传进一个 Bundle 对象。你可以使用 putString() 之类的方法,在这个 Bundle 对象中以键值对的形式保存状态信息。这时,如果系统销毁了 activity 所在进程后,用户又导航回你的 activity 时,系统就会传递这个 Bundle 对象给 onCreate() 方法,这样你就可以恢复出你在 onSaveInstanceState() 中保存的 activity 状态。如果没状态信息需要恢复,那么传递给 onCreate() 方法的 Bundle 对象为 null

注:因为很多情况下是不需要保存 activity 的状态信息的(比如用户按了返回键就是想关闭当前 activity ),所以并不能保证在你的 activity 被销毁前一定会调用 onSaveInstanceState() 方法。如果调用了该方法,一般是在 onStop 方法之前且可能在 onPause 之后调用。

尽管如此,即使你没做任何操作或没有实现 onSaveInstanceState() 方法,你的 activity 状态也能通过 Activity 类里面默认实现的 onSaveInstanceState 方法恢复出来。特别是会为布局中的视图( View )默认调用 onSaveInstanceState 方法,并在这个方法中允许每一个视图提供它需要恢复的任何信息。几乎每一个 Android 框架中的 widget 都视情况实现了这个方法,这样的话,这些 UI 的可视变化将会自动保存并在 activity 重建时被恢复。例如,一个 EditText 控件保存用户输入的内容,一个复选按钮保存它是否是被选中。你要做的只是给每一个你需要保存状态的 widget 提供一个唯一的 ID (用 android:id 属性)。如果一个 widget 没有 ID 的话,它将无法保存状态。

尽管默认实现了 onSaveInstanceState 方法来保存 activity UI 中重要的数据,但你也可以自己覆写它来保存一些额外的数据。比如,你可能需要保存一些在 activity 生命周期里更改的一些成员数据(比如一些与恢复 UI 有关、用来保存 UI 数据,但在默认情况下并没有被恢复的成员数据)。

因为默认实现的 onSaveInstanceState 方法帮助我们保存了 UI 的状态信息,所以如果需要通过覆写来保存额外信息时,你需要在做其它工作前先调用父类的 onSaveInstanceState 方法。

注:因为 onSaveInstanceState 方法不一定会被调用,所以你应该只是用它来保存一些 activity 的转换过程状态(即 UI 的状态),而不能用来保存永久性数据。但你可以用 onPause 方法在用户离开 activity 时来保存永久性数据,比如需要保存到数据库的数据。

有一个很好的方法可以用来检验应用程序保存状态的能力,就是简单地旋转你的设备来改变屏幕的方向。因为当屏幕方向改变时,系统为了给新的方向提供一个可能合适的代替资源,会销毁 activity 并新建一个新的。由于这个原因,你的 activity 是否能在其重新创建时完成保存状态就显得尤为重要,因为用户经常会在使用应用程序时旋转屏幕的。

处理配置的改变

很多设备的配置(如屏幕方向、键盘可用性和语言)会在运行时改变。当发生配置改变时, Android 会重启一个运行中的 activity ,即调用 onDestroy() 并马上调用 onCreate() 。重启 activity 可以让你的应用程序通过重载的过程自动用你提供的替代资源去匹配新的配置。如果你定义的 activity 能很好地处理这种情况,那么它能更好地适应 activity 生命周期中的一些异常事件。

处理配置变化(如改变屏幕方向)的最好方法就是用前面章节所讲的 onSaveInstanceState() 方法和 onRestoreInstanceState() 方法(或 onCreate() 方法)来保存你的应用程序状态。

更多关于运行时配置改变和如何处理它们的讨论,可参见“处理运行时改变”章节。

 

协调多个 Activity

当一个 activity 启动另一个 activity 时,这两个 activity 都发生生命周期的状态转换。第一个 activity 会暂时和停止(当它仍然在后台可见时,它是不会停止的),而第二个 activity 则会创建。如果这两个 activity 在磁盘中或其它地方共享了数据,那么一定要记住第一个 activity 在第二个 activity 创建前并不会完成停止。相反,创建第二个 activity 的进程会与停止第一个 activity 的进程同时进行。

Activity 的生命周期回调方法已经定义得很好了,特别是在两个运行在同一个线程的 activity 互相启动。下面是当 activity A 启动 activity B 时处理的顺序:

1.         执行 activity A onPause()

2.         按顺序执行 activty B onCreate() onStart() onResume() ,这时 activty B 获取到用户的焦点

3.         如果 activty A 在屏幕上不可见,那么执行 activty A onStop() 方法

对生命周期回调方法执行顺序的预计,可以让你管理好从一个 activty 向另一个 activty 转换时的一些信息。比如,如果你必须在第一个 activty 停止时向数据库中写入数据让第二个 activty 读取,那么你就应该在应该在 onPause 方法中向数据库写入数据,而不是在 onStop 方法中。(因为当 activty 在后台还是可见时,是不会执行 onStop 方法的 @ Sam


 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值