各位看客要多给意见,有什么理解错的地方一定要指出来,please!
Activities
Activity是一个提供了显示的构件,用户可以用它交互。如打电话,照相,发邮件或浏览一个地图。每一个有一个窗口用来绘制用户接口。该窗口一般充满整个屏幕,但是也可能比屏幕小,浮在其他窗口的上面。
一个应用程序通常由多个activity组成,它们彼此松散的绑定在一起。通常,应用程序会指定一个activity作为”main”activity。这个activity往往在加载程序的第一时间呈献给用户。每个activity可以启动其它的activity来执行不同的动作。同一时间一个activity启动,先前的activity就会停止(stop),但是系统会将其保存在一个栈中(back stack)。当新的activity启动,它将被压入栈中并放弃用户焦点。这个back stack遵守后进先出的机制,因此,当用户完成了对当前activity的操作,按下BACK键时,当前的acivity就会弹出栈(然后destroyed),先前的activity恢复(resume)。(更多内容见文档)
当一个activity因一个新的activity启动而停止时,通过activity生命周期回调函数来通知状态的改变。这个activity或许会有多个回调函数接到通知,因为状态的改变—--无论是系统在(create),停止(stop),恢复(resume),或销毁(destroy)它-----每个回调函数提供给你了在activity状态改变时适合执行何种工作的机会。当activity恢复的时候,你可以请求必要的资料和恢复被打断的动作。
如何创建一个Activity:
创建一个Activity:
必须继承自Activity类,并且实现它的回调函数,这些回调函数是当activity在各个生命周期状态转换时由系统调用,比如activity被创建(create),停止(stop),恢复(resume),或销毁(destroy)。
两个最为重要的回调函数是:
onCreate():
你必须实现这个方法,系统在创建activity时调用。在你的实现中,你应该初始化你的activity的重要构件,最为重要的是,在这里你必须调用setContentView()来设置你的activity的用户接口的布局(layout)
onPause():
系统调用该方法的第一迹象是,用户正在离开你的activity(虽然他不总是意味着被销毁)。在这里你应该保证所有需要存储的改变已记录(因为用户可能不再回来...).
实现一个用户接口:
activity的用户接口有一个views的层次化的体系提供----继承自View类的对象。每个view控制一个在activity窗口中的特定的矩形空间并且可以与用户的交互做出反应。比如,一个view可以是一个按钮,该按钮在被用户触摸时执行一个动作。
Android已经提供了许多已经构建好的view。你可以用它们来设计和组织你的布局。”Widgets”是可以在屏幕上可视(和可交互)的view,如按钮(button)、输入框(text field)、checkbox或者只是一幅图片。”Layout”是继承自ViewGroup的view,它们为子view提供了独特的布局模型,如linear layout(线性布局),grid layout(表格布局)或relative layout(相对布局)。你可以继承View和ViewGroup类来定义自己的 widget,并将其使用到你的activity布局中。
用views定义布局的最普通的方法是使用资源文件中的XML布局文件。这样,你可以保持界面设计与定义你的activity行为的源代码分离的设计理念。你可以传递布局文件的ID给setContentView()来设置该布局为你的activity的UI。当然,你也可以创建一个新的View,并将新的View插入到ViewGroup来构建一个新的体系结构,然后将根ViewGroup传递给setContentView()。
在manifest文件中声明activity:
你必须在manifest文件中声明你的activity来使系统接受它。
<activity>标签有很多属性,你可以将它们包含到元素中,来声明各种属性,如activity的标签(label)或图标(icon)或者UI的主题(theme)。
一个<activity>元素可以指定各种过滤器----用<intent-filter>元素---来声明其他的应用程序构件如何激活它。如下:
<action>元素指出这是应用程序的”main”入口。<category>元素指出这个activity应该被放到系统应用程序luncher(使用户可以启动这个activity)。
如果你打算让你的应用程序自用(self-contained),不允许其他的应用程序激活它的activity,你就不需要任何其它的过滤器。只有一个activity应该有”main”action和”launcher”类。如前边的例子,你不希望让其他应用程序使用的activity应该没有intent过滤器,你可以用explicit intent来启动它们。(下一节描述)
如果你希望你的activity对别的应用程序(或你自己)发来的implicit intent作出回应的话,你必须为你的activity声明额外的intent过滤器。<intent-filter>必须包含<action>,可选择性的包含<category>或<data>。
启动一个Activity:
你可以用startActivity()来启动另一个activity,需要传递给方法一个Intent对象来描述你要启动的activity。这个intent或者指定你想启动的确切的activity或者描述你想执行的动作的类型(系统会为你选择合适的activity,该activity可以不在一个程序中)。Intent也可以携带一些activity启动后需要的数据。例如:
或许你的应用程序想要执行什么动作,如发送email,文本信息或状态更新。在这种情况下,你的应用程序或许没有执行这些动作的activity,你可以利用其他为应用程序的activity来执行这些动作。这就是intent有用之处----你可以创建一个描述了您想要执行的动作的intent,系统会为你从其他应用程序加载一个合适的activity。如果有多个这样的activity,用户可以选择其中一个。比如,如果你想用户发送一个email信息,你可以创建这样的intent:
被额外的添加给intent的EXTRA_EMAIL是一个字符串数组,该数组存放了你想发送的email的地址。如果一个email程序对该intent做出反应,它会使用额外提供的这个字符串数组,并将里面的地址填在“发送到”输入框,这种情况下,email程序的activity启动,在用户完成操作后,你的activity恢复。
启动一个获得结果的Activity:
有时候你希望从你启动的activity获得结果,在这种情况下通过startActivityForResult()来启动一个activity。为了获得从随后的activity获得结果intent,需要实现onActivityResult()回调函数。当随后的activity完成操作,它就会在一个Intent中将结果返回给onActivityResult()方法。
举例,或许你想你的用户pick一个通讯录:
这个例子显示了你如何在onActivityResult()中获得activity结果的基本的逻辑。第一个条件检查请求是否成功----如果成功,那么resultCode是RESULT_OK—---并检查相应的结果的请求是否可知---在这种情况下,resultCode匹配startActivityForResult()的第二个参数。在这里可以通过方法返回的数据获得activity的结果。
接下来,一个ContentResolver执行对一个content provider的查询。返回一个允许读取查询数据的游标(Cursor)。
关闭一个Activity:
你可以通过调用finish()方法关闭一个activity。你也可以通过调用finishActivity()方法关闭一个之前启动的独立的activity。
Note:在大多数情况下,你不应显式的用方法结束一个activity。Android系统管理着activity的生命周期,你不需要finish你的activity。
Activity生命周期:
通过实现回调函数管理你的activity的生命周期对开发一个健壮和扩展良好的程序来说是非常重要的。一个activity和其他的activity以及它的task和back stack共同影响着activity的生命周期。
一个activity可以以以下三个基本的状态存在:
Resumed:
这个activity在屏幕的最前面,并获得了用户的焦点。
Paused:
另一个activity在前景并拥有焦点,但是现在的这个activity仍可见。换句话说,在一个activity外层有另一个activity,外层的activity不完全透明或者并没有覆盖住全部的屏幕。一个paused activity 是完全存活的(Activity对象仍驻留在内存中,保存了所有的状态和成员信息,并保留了与窗口管理器的连接),但是在系统缺少内错的情况下可被kill。
Stopped:
这个activity彻底被另一个activity掩盖(这个activity在后台了)。一个stopped activity也是存活的。然而用户已经看不到它,并且在系统缺少内存的情况下可被kill。
如果一个activity被pause或stop,系统可以通过调用finish()函数或者kill它的进程来将其从内存中清除。当这个activity再次被打开时(被finish或kill后),必须重新create。
实现生命周期回调函数:
当activity在上述不同的状态间转换的时候,是通过各种回调函数来得到通知。所有的回调函数都是hook,你可以重写它们来在你的activity状态变化时完成合适的工作。如下的activity包含了各个基本的生命周期函数:
注意:在生命周期函数开始必须先调用父类方法,然后再做自己的工作。
Activity的生命周期包含三个循环:
- entire lifetime:
的entire lifetime在onCreate()和onDestroy()之间。你的activity应该在onCreate()中执行”全局”状态的设置(如定义layout),在onDestroy()中释放剩余的资源。比如你的activity有一个线程正在后台从网络上下载数据,你因该在onCreate()中创建这个线程并在onDestroy()中释放它。
- visible lifetime:
一个activity的visible lifetime在onStart()和onStop()之间。在此期间,用户可以在屏幕上看到这个activity,并可以与之交互。比如,当一个新的activity被启动时,当前的activity不再可见,这时候会调用该activity的onStop()方法。在这两个方法之间你可以持有需要将activity显示给用户的资源。比如说,你可以在onStart()中注册一个BroadcastReceiver来监视影响你的UI的变化,并在用户看不到显示的内容时在onStop()中注销它。在entire lfetime中因为activity可能在可视和隐藏之间多次转变,所以onStart()和onStop()或许会多次调用。
- foregroundlifetime
一个activity的foreground lifetime在onResume()和onPause()之间。在此期间activity在所有其它activity的前面,并获得了用户焦点。一个activity可能会在前台(foregroud)和后台间转换。比如说,当设备休眠或者一个对话框出现时onPause()被调用。因为这个状态转变的过于频繁,因此在这两个方法中的代码应该是轻量级的,这样避免了用户的等待。
下图显示了activity的生命周期:
下面列出了各方法更多的细节:
方法名 | 描述 | 可被kill? | Next | ||
onCreate() | activity 第一次创建时调用.在这里你应该做所有正常的静态设置—创建view或把数据绑定到list等。这个方法传递一个包含activity先前状态(如果这个状态是被捕捉的)的Bundle对象。 | No | onStart() | ||
| onRestart() | activity 被stop时调用 | No | onStart() | |
onStart() | Activity对用户可见时调用。 | No | onResume() | ||
| onResume() | 开始于用户交互时(获得用户焦点)调用. 在此时activity在 activity stack的顶端 | No | onPause() | |
onPause() | 在系统要resume另一个activity时调用. 该方法常被用于处理一些改变了的持久化数据的保存,停止动画或者其他消耗CPU的动作。它应该执行的尽可能快,因为下一个activity只有在该方法返回时才能被resume。 | Yes | onResume() | ||
onStop() | Activity对用户不在可见时调用. | Yes | onRestart() | ||
onDestroy() | Activity被销毁时调用. | Yes | nothing |
保存activity 的状态:
在前面的介绍中简单的提到了当一个activity被pause或stop时,activity的状态已被保存。这是因为在此时Activity对象仍然在内存中---它所有的关于成员的信息和当前的状态仍然是存活的。因此,为了在activity返回到前台时(当它resume时)用户对这个activity的所有改变还存在,需要将这些改变保持在内存中。
然而,当系统为了回收内存而销毁了这个activity时,这个Activity对象被销毁了,系统不能再简单的将其完好无损的resume。如果用户希望用导航键back回到该activity,系统只能在重新创建Activity对象。此时用户并不知道系统销毁了这个activity后有重新创建了一个,因此或许会认为这个就是以前那一个。在这种情况下,你通过实现一个附加的回调函数来保存你的activty的状态的信息,然后在系统重新创建它时时恢复这些信息。通过这种方式你可以确信你的activity的重要信息被保护。
可以保存你的activity的当前状态的回调函数是onSaveInstanceState()。这个回调函数在activity容易被销毁的时候调用,并会传递一个Bundle对象给它。你可以将activity的状态信息以键值对的形式,用putString()等方法存放到Bundle中。如果系统kill了你的activity的进程,而用户用back键想回到该activity,系统会将这个Bundle传递给onCreate(),这样你就可以在恢复在onSaveInstanceState()中保存的数据了。如果没有信息要恢复,传递给onCreate()函数的Bundle将会是null。
注:在你的activity被销毁之前,onSaveInstanceState()方法并不能保证一定会运行,因为有这样一种情况就是状态信息根本不需要保存(如用户想用BACK键离开时,他希望的是关闭这个activity)。这个方法往往在onStop()和onPause()前调用。
然而,即使你没有实现onSaveInstanceState(),一些activity的状态在Activity类的默认的onSaveInstanceState()实现中保存。特别的,拿布局中的View的对onSaveInstanceState()来说,它允许每个view为它自己提供需要保存的信息。在Android框架中几乎所有的widget都视情形实现了这个方法。因此所有UI的可见的变化都被自动保存并在activity重新创建时恢复。比如说,EditText widget会保存所有用户输入的文本,CheckBox widget会保存它是否被选中。你唯一需要做的是为每个你想保存其状态的widget设置一个独一无二的ID(使用android:id属性)。如果widget没有ID,它将不能保存它的状态。
虽然对onSaveInstanceState()的默认实现为你的activity的UI保存了有用的信息,但是你仍然需要重写该方法来保存一些额外的信息。比如说,你或许需要保存一些在activity生命期内改变的成员变量。
当你重写onSaveInstanceState()时,应该将在你所有的工作之前调用父类对该方法的默认实现。
注:因为onSaveInstanceState()方法并不一定被调用,你应该用它记录暂时的状态---你不应该用它存储persistent数据(比如要保存到数据库的数据)而是使用onPause()方法来存贮。
一个检测你的应用程序恢复状态的能力的好方法是旋转你的设备,这样屏幕的方向会发生改变。当屏幕方向改变时,系统会销毁并重新创建一个activity来申请备用的能适应新方向的资源。基于这个原因,当activity被重新创建是完全的恢复其状态是非常重要的,用户经常会在使用应用程序时会经常旋转屏幕。
处理配置信息的改变:
许多设备的配置在运行时常常改变(如屏幕方向,键盘是否可用,语言等)。当发生这种改变时,Android会重启一个运行的Activity(onDestroy()被调用,随后调用onCreate())。这种restart行为设计是为了使你的应用程序适应新的配置,这些配置是由你所提供的可替换的资源自动的重新加载你的应用程序得到的。如果你设计你的activity来妥善的处理这些事件,在activity的生命周期处理突发事件会很有弹性。
处理配置改变的最好方法是如前面一节讲述的那样用onSaveInstanceState()和
onRestoreInstanceState()(或onCreate())方法保存你应用程序的状态。
协调的activity:
当一个activity启动另一个时,它们都在经历生命周期的变换。当另一个activity被创建时,第一个activity pause并stop(虽然它在后台运行仍然可视时不会stop)。如果这些activity共享的数据被保存到硬盘或者光盘中,重要的是要理解,在第二个activity被创建前,第一个activity并未完全的stop。相反的,启动第二个activity的进程会与stop第一个的进程重叠。
Activity生命周期回调函数的调用顺序非常明确,尤其是两个activity在同一个进程中并且一个activity启动另一个时。下面当Activity A 启动 Activity B 时操作执行的顺序:
1、 Activity A的onPause()方法执行。
2、 Activity B的onCreate(),onStart()和onResume()方法执行。(这时Activity B获得了用户焦点)
3、 如果Activity A在屏幕上不在可见时,它的onStop()方法执行。
这种可预料的生命周期函数调用的次序使你能够管理从一个activity到另一个转换的信息。比如说,如果你在第一个activity stop时向数据库写入信息以便在第二个activity可以读取它,这时你应该在onPause()中写入数据库,而不是在onStop()时写入。