天天看点

Android SDK 中文文档计划 (0) 我的声明 - [Android SDK 中文文档]

应用程序基础 Application Components Activating components: intents Shutting down components The manifest file Intent filters Activities and Tasks Affinities and new tasks Launch modes Clearing the stack Starting tasks Processes and Threads Processes Threads Remote procedure calls Thread-safe methods Component Lifecycles Activity lifecycle 调用父类方法 Saving activity state Coordinating activities Service lifecycle Broadcast receiver lifecycle Processes and lifecycles 应用程序基础 Android应用程序由Java语言编写。Java代码被编译成Android Package文件(.apk)。一个.apk文件中的所有代码成为一个应用程序。 从很多方面来说,Android应用程序生活在他们自己的世界中: 默认情况下,每个应用程序在它自己的Linux进程中运行。当一个应用程序的任何代码需要被运行时,Android启动进程;当该进程不再需要并且其它应用程序需要系统资源时,Android停止进程。 每个进程拥有它自己的JVM,因此一个应用程序的代码和其它应用程序完全独立运行。 默认情况下,每个应用程序拥有一个唯一的Linux user ID。权限被设置成一个应用程序的文件只对该用户可见,只对该应用程序自身可见 --- 尽管有方法可以使它们对其它应用程序可见。可以让两个应用程序使用相同的user ID,则样它们可以看到对方的文件。为节约系统资源,拥有相同ID的应用程序也可以在相同的Linux进程中运行,共享一个虚拟机。 Application Components Android的一个核心特性就是:一个应用程序可以利用其它应用程序的元素。例如,如果你的应用程序需要显示图片,而有个人正好写了一个图片浏览程序,你可以直接去调用它。为达到这个目的,当一个应用程序的任一部分需要启动时,系统都应该能够启动它。因此,Android的应用程序是没有唯一的程序入口的,而是有系统可以运行的基本component。component可以分为4种: Activities 一个activity呈现了一个用户可以操作的可视化用户界面。例如,一个activity可以显示一个菜单列表或者显示一排图片。一个短信应用程序可以有一个显示联系人列表的activity,一个编辑信息的activity以及查看信息和设置的activity等等。一个应用程序可以由一个或多个activity组成。一般来说有一个activity是最先显示的。从一个activity转移到另一个activity是通过在一个activity中启动另一个activity来完成的。 每一个activity有一个某人的窗口可以绘图。一般来说,该窗口填满整个屏幕,但是它也可以比屏幕小。一个activity也可以使用额外的窗口 --- 例如一个弹出对话框。 一个窗口的内容是由view体系来提供的。每个view控制窗口内一个特殊的的矩形区域。父view包含并且控制子view的排列。树叶view在它们控制的矩形内绘画并且负责响应用户在它们负责区域内的动作。因此,view是activity和用户交互的区域。例如一个view可以显示一个图片,当用户点击它的时候完成一个动作。Android有许多内置的view --- 按钮、文本框,等等。一个view的体系通过Activity.setContentView()被放置到一个activity的窗口中去。content view指的是在体系根部的view对象。(详见用户界面文档部分。) Services 一个service不包含可见的用户界面,而是在后台无限地运行。例如,一个service可以在用户做其他事情时播放背景音乐,或者获取网络数据来供其它activity使用。service继承于Service类。以一个从一个播放列表中播放歌曲的媒体播放器为例。播放器程序可能有一个或多个activity,使得用户可以选择歌曲和播放它们。然而,音乐的播放本身不能被一个activity来处理,因为用户希望它们离开播放器去做别的事情时音乐仍然在播放。为了使音乐不停播放,播放器activity可以启动一个后台service。系统可以让音乐播放service在启动它的activity离开屏幕时继续运行。就像activity和其它的component一样,service在应用程序进程的主线程里运行。它们一般会启动另一个线程来做消耗时间的任务(例如音乐播放),从而不会阻塞其它的component和用户界面。(详见Processes and Threads). Broadcast receivers 一个broadcast receiver是一个接收广播消息并作出回应的component。很多广播消息起源于系统代码 --- 例如,当时区变化、电池电量不足、刚刚拍了一张照片的时候都会产生消息广播。应用程序也可以产生广播消息 --- 例如,通知其它程序某些数据已经下载完成等等。 一个应用程序可以有任意多个broadcast receiver来响应它认为重要的消息。所有的receiver继承于BroadcastReceiver基类。 broadcast receiver没有界面。但是它可以启动一个activity来响应它们收到的信息,或者使用NotificationManager来警告用户。Notification有很多种:闪烁背光、震动、播放声音等等。一般来说它们在状态栏上有一个图标,在这个图标上显示消息。 Content providers 一个content provider使一个应用程序的某个数据集合可以为其他应用程序所用。这些数据可以存在文件系统中、SOLite数据库中或者其他地方。content provider继承于ContentProvider基类并实现一系列方法来使其他应用程序可以存取它控制的类型的数据。但是应用程序不会直接调用这些方法,而是使用一个ContentResolver对象来进行。一个ContentResolver可以和任何content provider来进行对话。它和content provider合作来管理进程间的通信。详见Content Provider文档。 当Android检测到有一个需要被某个component来处理的请求,它就会保证该component的应用程序进程运行,在需要的时候启动它,并且保证有一个可用的component实例,当需要的时候创建它。 Activating components: intents content provider在接收到ContentResolver的请求时被激活。另外的三种component — activity, service和broadcast receiver是被称为intents的异步消息激活的。一个intent是一个Intent对象,它保存了消息的内容。对于activity和service来说,它指定了请求的操作名称和待操作数据的URI等等。例如,它可以传递给activity一个请求,让它显示一张图片或者让用户编辑一些文字。对于broadcast receiver来说,Intent对象指定了操作的名称。例如,它可以把“照相机按钮被按下”这个事件告诉感兴趣的应用程序。激活每一种component有不同的方法: 一个activity通过将Intent对象传给Context.startActivity()或者Activity.startActivityForResult()的方式来启动。响应的activity可以通过getIntent()方法看到启动它的intent。Android调用activity的onNewIntent()方法来将后续的intent传递给它。一个activity通常会启动另一个。如果它期待从它启动的activity中得到结果,则调用startActivityForResult()。例如,如果它启动了一个让用户选择一张图片的activity,它可能期待返回被选择的图片。该结果是放在一个Intent对象中,通过调用onActivityResult()的方式来返回的。一个service通过将Intent对象传给Context.startService()的方式来启动的(或者将新的指令传给一个service)。Android调用service的onStart()方法并将Intent对象传递给它。类似的,一个intent可以被传递给Context.bindService()以建立一个在主调component和目标service之间的链接。service在onBind()调用中接收到Intent对象。(如果service没有启动,bindService()可以选择启动它。)例如,一个activity可以和一个音乐播放service建立链接,这样它就可以为用户提供控制播放的界面。activity调用bindService()来建立连接,然后调用service定义的方法来影响播放。关于绑定service详见Remote procedure call一节。 一个应用程序可以通过把一个intent对象传递给Context.sendBroadcast(), Context.sendOrderedBroadcast(), and Context.sendStickyBroadcast()这样的方法来启动一个broadcast。Android将intent通过onReceive()方法来传递给所有感兴趣的broadcast receiver。 Shutting down components 一个content provider仅当它响应来自ContentResolver的请求时有效。一个broadcast receiver仅当它响应广播消息的时候有效。因此它们不需要显式的关闭。另一方面,Activity和service可能长期存在。因此android有关闭activity和service的方法: 一个activity可以使用finish()方法来关闭。一个activity可以使用finishActivity来关闭另一个activity。 一个service可以使用stopSelf()方法来关闭,或者Context.stopService(). component也可以在不需要的时候或者内存紧张的时候被系统关闭。在后面的Component Lifcycles一节中会介绍这一点。 The manifest file 在android启动一个应用程序的component之前,它必须知道这个component的存在。因此,应用程序在manifest文件中声明了它的component。manifest文件和代码、资源文件等一起被打包在.apk文件中。 manifest文件是一个结构化的xml文件。任何应用程序中都叫做AndroidManifest.xml。它还有其他一些功能,例如指定应用程序需要连接的库,以及需要的权限。 manifest的主要功能还是告诉android这个应用程序的component,例如一个activity可以像这样定义:

. . . 元素的name属性指定了实现该activity的Activity子类。icon和label属性指向该activity呈现给用户的图标和标签。其他的component也是用类似的方法来定义 — ,和分别定义了service,broadcast receiver和content provider。在manifest中未声明的Activity, service以及content provider对于系统是不可见的,因此永远不会被运行。但是,broadcast receiver可以在manifest中定义,也可以动态的在代码中被创建并是用Context.registerReceiver()来注册到系统中。关于manifest文件的结构详见The AndroidManifest.xml. Intent filters Intent对象可以显式的指定一个目标component。如果这样的话,android会找到这个component(基于manifest文件中的声明)并激活它。但如果一个目标不是显式指定的,android必须找到响应intent的最佳component。它是通过将Intent对象和目标的intent filter相比较来完成这一工作的。一个component的intent filter告诉android该component能处理的intent。intent filter也是在manifest文件中声明的。例如:

. . . 第一个filter — action "android.intent.action.MAIN"和 category "android.intent.category.LAUNCHER"表示该activity应该是在主程序启动界面中出现的一个程序,用户可以直接在主界面中启动它。第二个filter声明了一个可以操作某种特定类型数据的action。一个component可以有任意多数量的intent filter,每一个都声明了不同的能力集合。如果它没有任何filter,那么它只能在显式指定名字的方式下启动。对于在代码中创建和注册的broadcast receiver,intent filter直接被实例化为一个IntentFilter对象。其他的filter在manifest中设置。关于Intent Filter 详见单独的文档。 Activities and Tasks 前面提到了,一个activity可以启动另一个activity,包括定义在不同的application中的activity。假设你希望用户显示某个地方的地图。已经有一个activity可以做这件事情,因此你的activity只需要把一个intent object设置好并传递给startActivity()就可以了。地图浏览器会显示地图。当用户按下BACK键,你的activity又会重新显示。 对于用户来说,地图浏览器就好像是你的应用程序中的一部分,即使它是在另一个应用程序中定义并运行的。android将这两个activity放在同一个task中来让用户感觉它们是同一个程序。简单的说,一个task就是用户感觉上的一个application。它是一组相关的activity,组织在一个stack中。我们称开始任务的activity为根activity。一般来说,根activity是用户在程序启动器中启动的。在stack顶端的activity是当前正在运行的activity。当一个activity启动另一个时,新的activity被推入stack并成为运行的activity。前一个activity仍然保存在stack中。当用户按下BACK键时,当前的activity被拖出,前一个activity成为运行activity。 stack储存的是对象,因此如果一个stack包含了同一个Activity类的不同实例,它们会放在不同的地方。stack中的activity只能推入和拖出,而不能进行其它的顺序变化。 A task is a stack of activities, not a class or an element in the manifest file. So there's no way to set values for a task independently of its activities. Values for the task as a whole are set in the root activity. For example, the next section will talk about the "affinity of a task"; that value is read from the affinity set for the task's root activity. 一个task是多个activity构成的stack,而不是在manifest文件中的一个类或者一个元素。因此没有办法来设定一个task的属性而不管它的activity。一个task的所有属性值是在根activity中设定的。例如task的affinity。它是从根activity中读取的。 一个task中所有的activity作一个整体来运动。整个task可以被提到前台或者放到后台。例如,当前的task有4个activity。用户按下HOME键,进入程序启动器,然后选择一个新的应用程序(事实上就是一个新的task).当前的task进入后台,新task的根activity被显示。然后用户又进入主界面启动前一个应用程序,则那个有4个activity的task进入前台。当用户按下BACK键时,不会显示刚才那个task的root activity,而是将当前task的stack顶部activity移除并显示前一个activity。 以上是activity和task的默认行为。但是也有其他的方法来改变这种行为。activity和task之间的联系和行为是由启动activity的intent对象中的标签以及manifest文件中元素的属性值共同影响的。 主要的Intent标签值有以下几种: FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_CLEAR_TOP FLAG_ACTIVITY_RESET_TASK_IF_NEEDED FLAG_ACTIVITY_SINGLE_TOP 主要的属性有: taskAffinity launchMode allowTaskReparenting clearTaskOnLaunch alwaysRetainTaskState finishOnTaskLaunch 下面我们来看看它们的作用。 Affinities and new tasks 默认情况下一个应用程序中所有的activity相互之间都有一个affinity. 对于一个activity也可以设置一个单独的affinity. 当启动activity的Intent对象包含FLAG_ACTIVITY_NEW_TASK标志,并且 activity的allowTaskReparenting属性为"true". FLAG_ACTIVITY_NEW_TASK 标志一个新的activity在默认情况下在调用startActivity()的activity所属的task中启动,被推入和启动它的activity相同的activity栈中。但是,如果传给startActivity()的Intent对象包含 FLAG_ACTIVITY_NEW_TASK标志,则系统寻找一个不同的task来管理改这个新的activity。一般来说是一个新的task,不过也不一定是。如果已经存在一个和新activity有相同affinity的 task,则该activity在这个task中启动。否则启动一个新的task。 allowTaskReparenting属性如果一个activity的allowTaskReparenting属性为"true",它可以从它启动时所在的task移动到另一个它具有affinity的task中。例如,假设一个旅游助理软件中有一个activity是用来报告天气的。它和这个应用程序中的其它activity有相同的affinity(默认affinity)并且允许reparenting。某个activity启动天气预报器,因此它和这个activity属于同一个task。然而,当旅游应用程序进入前台时,天气报告器会被重新分配到旅游应用程序的task中去。 如果对于一个用户来说,一个apk文件包含了多于一个的应用程序,很可能你需要为不同的activity赋予不同的affinity。 Launch modes 元素的launchMode属性可以有4种值: "standard" (the default mode) "singleTop" "singleTask" "singleInstance" 这些模式相互之间的区别有以下4个方面: 哪一个task将持有响应intent的activity。对于"standard"和"singleTop"模式,是由初始化intent并且调用startActivity()的task来持有 — 除非intent对象有FLAG_ACTIVITY_NEW_TASK标志。在后一种情况下,使用一个新的task。另一方面,"singleTask"和"singleInstance"模式下的activity永远处于task的根部。它们定义一个task,永远不会在另一个task中启动。 一个activity是否可以有多个实例。一个"standard"或者"singleTop"的activity可以有多个实例。它们可以属于多个task,一个task可以有同一个activity的多个实例。而"singleTask"和"singleInstance"的activity只能有一个实例。由于它们位于task的根部,这个限制意味着在一个设备上不会有多余一个的instance。 该实例能否和其它activity共用一个task。一个"singleInstance"的activity只能作为一个task中的唯一的activity。如果它启动了另一个activity,后者会启动到一个不同的task中去,不管它的启动模式如何 — 就好像设定了FLAG_ACTIVITY_NEW_TASK一样。在其他方面"singleInstance"模式和"singleTask"模式都一样。其它三种模式都允许多个activity属于它们所在的task。一个"singleTask"activity永远位于task的根部,但是它可以启动其它的activity并分配到它所在的task。"standard"和"singleTop"的activity可以在一个stack的任意位置出现。 在一个新的intent到达时,是否需要启动该类的一个新的实例来处理它。对于"standard"模式,会启动一个新的实例来处理每个新的intent。每个实例只处理一个intent。对于"singleTop"模式,重用一个已存在的实例来处理该intent,如果它存在于目标task的activity stack的顶端。如果它不在顶端,则不重用它,而是为新的intent创建一个新的实例并推入stack顶端。例如,假设一个task的activity stack有依次A,B,C,D 4个activity,A位于根部,D位于顶端,stack为A-B-C-D。这时候一个intent到达并需要一个类型D的activity。如果D的启动模式为"standard",则启动一个新的D实例,stack变为A-B-C-D-D。但是,如果D的启动模式为"singleTop",则当前的D将处理该intent,stack仍然保持为A-B-C-D。另一方面,如果到达的intent需要一个类型B的activity,则无论B的模式为"standard"还是"singleTop",都会启动一个B的实例,则stack将会成为A-B-C-D-B。如前所述,对于"singleTask"和"singleInstance"的activity,不可能有多余一个的实例,因此该唯一的实例需要处理所有的intent。一个"singleInstance"的activity永远位于stack顶端(因为它是stack中唯一的activity),因此它永远处于可以处理intent的位置。但是当一个"singleTask"的activity不位于stack顶端时,intent直接被忽略掉.(尽管它被忽略了,但是它的到达会使处理它的task进入前台). 当一个已经存在的activity需要处理一个新的intent的时候,这个intent对象被使用onNewIntent()传递到activity中. (开始启动activity的intent对象可以使用getIntent()方法来取得。)注意当一个新的Activity实例被创建来处理intent的时候,用户总是可以用BACK键来返回到上一个状态(上一个activity)。但当一个已有的Activity实例处理intent的时候,用户不能用BACK键回到之前的状态。关于启动模式详见元素的文档。 Clearing the stack 如果用户离开一个task很长时间,系统会将该task除了根activity之外所有的其它activity清除掉。当用户返回到该task,它和用户离开的时候一样,只是只剩下了初始的activity。这里的想法是,在一段时间以后,用户可能丢掉了他们原来想做的事情,并且回到这个task中来做新的事情。这是默认的做法。有一些activity属性可以用来改变这个做法: alwaysRetainTaskState 属性 如果根activity的该属性为"true",则上述默认行为不会发生。该task将保留所有的activity. clearTaskOnLaunch 属性 如果根activity的该属性为"true",则只要用户离开就将除根activity之外所有activity清除。 The finishOnTaskLaunch attribute 该属性和clearTaskOnLauch行为类似,但它作用于单个activity,而非整个task。它可以导致包括根activity关闭。当设为"true"时,该activity只在当前会话中属于该task。当用户离开再返回时该activity不再有效。 还有一种方法来强制使activity被从stack上移除。如果intent对象包含FLAG_ACTIVITY_CLEAR_TOP标志,并且目标task已经有一个可以处理该intent的activity,则所有在该activity之上的activity都被清除掉。如果该activity的启动模式为"standard",它也会被从stack上移除,并且启动一个新的实例来处理该intent。这是因为当启动模式为"standard"时,总是会启动一个新的实例来处理Intent。 FLAG_ACTIVITY_CLEAR_TOP常常和 FLAG_ACTIVITY_NEW_TASK一起使用,可以找到另外一个task中的activity并把它放在一个可以响应intent的位置。 Starting tasks 当需要一个activity作为整个应用程序的进入点时,我们给它一个intent filter,其中action为 "android.intent.action.MAIN",category为"android.intent.category.LAUNCHER"。这种filter使得一个图标和一个标签显示在程序启动器中,使用户可以启动该task或者返回到该task。返回该task这种能力时很重要的:用户需要能够离开一个task并且一段时间后能返回。因此,两种永远启动新的task的启动模式"singleTask"和"singleInstance"应该只在具有MAIN和LAUNCHER的filter的activity中使用。设想如果没有该filter的话,一个intent启动了一个"singleTask"的activity,启动了一个新的task,用户在该task中工作了一段时间。用户按下HOME键离开它,该task现在家屏幕后面。由于它不在应用程序启动器中,用户没有办法来返回到它。 FLAG_ACTIVITY_NEW_TASK标签也有一个难题。如果该标签导致一个activity启动一个新的task,然后用户按下HOME键离开,必须有一种方法使得用户可以回到它。有些程序(例如通知管理器)总是在外部task中启动activity,因此它们总是使用FLAG_ACTIVITY_NEW_TASK标志。如果你有一个activity可以被外部程序用这个标签启动,注意要让用户有一个方法来返回原来的位置。当你不想要用户能够返回到前一个activity时,将元素的"finishOnTaskLaunch"设为"true"。 Processes and Threads However, you can arrange for components to run in other processes, and you can spawn additional threads for any process. 当一个应用程序的第一个component需要运行时,Android为它启动一个包含一个线程的Linux进程。默认情况下,一个应用程序中所有的component都在该进程和线程中运行。 你也可以让component在其他的进程中运行或者在进程中创建其它的线程。 Processes 一个component在进程中运行,该进程是被manifest文件所控制的。component元素 — , , , — 每一个都有一个process属性来指定该component运行的位置。这些属性可以被设置成每个component在各自不同的进程中运行,或者某些component在同一个进程中运行。它们也可以被设置成不同的应用程序的component在同一个进程中运行 — 只要这些应用程序共用同一个Linux user ID。元素也有一个process属性用来设置所有component的默认属性值。 所有的component都在一个进程中的主线程中初始化,对component的系统调用是由该线程调度的。对每一个实例有不同的线程。因此,实例方法 — 例如View.onKeyDown()这样的报告用户动作和生命周期通知的方法 — 永远在主线程中运行。这意味着component不应该做耗时的和阻塞的操作,而是应该创建其它的线程来做。 Android可以决定在资源不足时关闭一个进程。在进程内运行的应用程序的component随之关闭。当有事情要做时,一个进程又会启动。 Android在决定关闭哪个程序时会计算它们对用户的相对重要性。例如,不可见的程序比可见的重要性低。因此是否关闭一个进程的决定取决于component的状态,下一节我们讨论之。 Threads 即时你可以将应用程序限制在一个单个进程中,有可能你也需要启动一个线程来做一些后台工作。由于用户界面需要永远快速响应,负责activity的线程不应做耗时多的工作。这些工作应该放在另一个线程中做。线程使用Java Thread对象在代码中创建。Android提供了一些类来方便管理线程 — Looper用来在线程中运行一个消息循环,Handler用来处理消息,HandlerThread用来在消息循环中设置一个线程。 Remote procedure calls Android有一个轻量级的RPC机制。在这里一个方法在本地调用,但在远程执行(在另一个进程中),再将结果返回给调用者。这要求将方法调用和它的数据分解到操作系统可以理解的级别,将其从本地进程和地址空间转移到远程进程和地址空寂那,然后重新组织和调用。返回值必须从相反的方向传送。Android提供了做这些事情的代码,所以你可以集中精力来定义和实现RPC接口本身。 一个RPC接口可以只包含方法。所有的方法都是同步运行(本地方法阻塞直到远程方法完成),即使没有返回值。简而言之,该机制如下:首先使用IDL(interface definition language)定义一个RPC接口。aidl工具从这个声明来生成一个Java接口定义,该定义对本地和远程进程都有效。它包含两个内部类,如下图所示:

Android SDK 中文文档计划 (0) 我的声明 - [Android SDK 中文文档]

内部类具有管理在IDL中定义的rpc接口的代码。两个内部类均实现IBinder接口。其中一个是系统本地内部调用的,另外一个成为Stub的扩展了Binder类。除了IPC调用的内部代码外,它还包含RPC接口的声明。你需要继承Stub来实现这些方法,如图所示。

一般的,这些远程进程将被一个服务来管理(因为一个服务可以通知系统一个进程和其他进程的连接)。该服务既有aidl工具生成的接口文件又有实现RPC方法的Stub的子类。该服务的客户将只有aidl工具生成的接口文件。

下面是一个service和一个client建立连接的过程:

  • service的client(在本地端)实现onServcieConnected()和onServiceDisconnected()方法,这样它们就可以在连接成功和断开成功后被通知。它们可以通过bindService()来建立连接。
  • service的onBind()方法将接受或拒绝连接,根据它接收到的intent(发送给bindService()的intent)。如果连接接受了,它将返回一个Stub类的实例。
  • 如果service接受了连接,Android调用client的onServiceConnected()方法并将其传给一个IBinder对象 — 由service管理的Stub子类的一个代理。通过该代理,client可以调用远程方法。

Thread-safe methods

在一些情况下,你实现的方法可能被多于一个线程来调用。因此它们必须被写成线程安全的。

这一点对于可以远程调用的方法来说一般都是正确的——就像上一节所说的RPC机制一样。当一个IBinder对象实现的方法在IBinder的同一个进程中调用时,该方法是在调用者的线程中进行的。但是,当该调用在另外一个进程时,该方法是在Android维护的一个位于IBinder进程中的线程池中的一个线程中调用的;它不是在进程的主线程中调用的。例如,一个service的onBind()方法会被该service的进程中的主线程调用,使用onBind()返回的对象实现的方法(例如,一个实现RPC方法的Stub子类)将被从线程池中的线程调用。由于service可以有多于一个的client,多于一个的线程池线程可以同时处理相同的IBinder方法。因此IBinder方法必须被实现为线程安全的。

类似的,一个content provider可以接收另一个进程中的请求。虽然ContentResolver和ContentProvider隐藏了进程间通信的细节,ContentProvider响应请求的方法——query(),insert(),delete(),update()以及getType()——是从content provider进程的线程池中运行的,而不是进程的主线程。由于这些方法可以被任意多线程同时调用,因此它们也需要被实现为线程安全的。

Component Lifecycles

应用程序的component有生命周期—从一开始Android初始化它们使它们响应intent到结束时它们被销毁。在其间,它们有时候有效有时候无效,对于activity来说,有时候对用户可见有时候不可见。本节讨论activity,service以及broadcast receiver的生命周期——包括它们可能处于的状态,在状态转换时通知你的方法,和这些状态对进程的终止和实例销毁的作用。

Activity lifecycle

Activity有三种基本状态:

  • 当它在屏幕前台时(出于当前task的activity stack的顶端)状态为active或running。该activity是用户动作的焦点。
  •  如果它失去焦点但仍然对用户可见,它的状态为paused。也就是说,另一个activity位于其顶端,并且那个activity是透明的或者未覆盖整个屏幕,因此该暂停的activity仍然有一部分可见。一个暂停的activity完全是活着的(它保存了所有的状态和成员信息并且和窗口管理器连接),但是可以在极端内存不足时可以被系统关闭。
  • 当它被另一个activity完全覆盖时状态为stopped。它仍然保存了所有的状态和成员信息。然而,它对用户是不可见的因此它的窗口被隐藏了,并且在系统在其它地方需要内存的时候常常kill掉它。

如果一个activity处于paused或stopped状态,系统可以将其从内存中去掉,可以告诉它关闭(调用它的finish()方法),或者直接kill掉它的进程。当它再次显示时,它必须完全重启并恢复到原来的状态。

当一个activity从一个状态转换到另一个状态,它会被通知,这是通过调用下面的函数完成的:

void onCreate(Bundle savedInstanceState)

void onStart()

void onRestart()

void onResume()

void onPause()

void onStop()

void onDestroy()

所有这些方法都可以被重写来做状态改变时应该做的工作。所有的activity必须实现onCreate()方法来做初始化工作。许多activity实现onPause()来确认数据修改并做好停止和用户交互的准备。

调用父类方法

activity的声明周期方法必须首先调用其父类版本。例如:

protected void onPause() {

    super.onPause();

    . . .

}

这7个方法一起定义了一个activity的生命周期。通过实现这些方法,你可以管理3个嵌套的生命周期循环:

  • 完整周期:从第一次调用onCreate()到一次onDestroy()。一个activity在onCreate()中做所有的"全局"状态的初始化设置,在onDestroy()中释放所有的资源。例如,如果它有一个后台线程从网络下载数据,它可以在onCreate()中创建该线程并在onDestroy()中停止该线程。
  • 可见周期:从一个onStart()到一个相应的onStop()。在这个周期内,用户可以在屏幕上看见该activity,虽然它不一定在前台与用户交互。在这两个方法之间,你可以维护显示activity所需要的资源。例如,你可以在onStart()中注册一个BroadcastReceiver来监视对UI有影响的变化,并在onStop()方法中将其unregister。onStart()和onStop()可以被调用很多次。
  • 前台周期:从一个onResume()到onPause()之间。在这期间,activity在所有其他屏幕上的activity之上并且在和用户交互。一个activity可以频繁的在resumed和paused状态之间切换——例如,onPause()在设备待机时或者当一个新的activity启动时被调用,onResume()在一个activity的返回值或者一个新的intent被发送的时候调用。因此,这两个方法中的代码应该是轻量级的。

下表阐述了这些循环以及activity状态转换的可能路径。彩色的椭圆为activity可以处于的状态。圆角矩形表示你可以实现的回调函数来实现在状态转换时进行的操作。

Android SDK 中文文档计划 (0) 我的声明 - [Android SDK 中文文档]

下表详细介绍这些方法的意义和在activity的生命周期中所处的地位:

方法 描述 是否能被kill 下一个状态
onCreate() 当activity被创建时被调用。这里你应该做所有的静态初始化工作——创建view,绑定数据等等。该方法有一个Bundle对象保存了activity的前一个状态,如果该状态被获取。 onStart()
onRestart() 当activity被stop以后,start之前。执行之后永远会执行onStart()。 onStart()
onStart() 当activity变为对用户可见时被调用。如果activity进入前台则继续执行onResume(),如果它被隐藏则执行onStop()。 onResume()或onStop()
onResume() 当activity开始和用户进行交互时被调用。在这一个时间点activity处于activity stack的顶端,处理用户输入。下一个状态永远为onPause() onPause()
onPause() 当系统将要恢复另一个activity时调用。该方法一般用来保存一些数据,停止消耗cpu的操作例如动画等。它应该迅速完成工作,因为下一个activity要等到它返回才能恢复。如果该activity进入前台则继续调用onResume(),如果变为不可见则继续调用onStop(). onResume()或onStop()
onStop() 当activity对用户不再可见时调用。当activity被销毁或者另一个activity启动并覆盖于之上时调用。如果该activity回来和用户交互则调用onRestart(),如果该activity将被销毁则调用onDestroy()。 onRestart()或onDestroy()
onDestroy() 在activity被销毁之前被调用。这是activity最后一个接收的调用。它可以在activity正在结束时被调用(程序中调用了finish()),或者因为系统为节约空间而kill它。可以用isFinishing()方法来区分二者。

注意该表格的"是否能被kill"项。它表示系统是否能够在该方法返回后的任意时间Kill掉进程,而不执行该activity的其它代码。有三个方法onPause(),onStop()和onDestroy()该项为"是"。由于onPause()是3个中的第一个,因此它是唯一一个在进程被kill之前一定会被调用的—onStop()和onDestroy()可能不被调用。因此,你应该使用onPause()来保存任何永久性数据。

该项为"否"的方法在它们被调用时保护该activity进程不被Kill。因此一个activity的可Kill状态为从onPause()返回一直到onResume()被调用之前。直到下一次onPause()返回之前它都不能被kill。 在后面的进程和生命周期一节中会讲到,一个不处于可kill状态的activity仍然可以被系统kill掉——但只会发生在资源耗尽的极端的情况下。

Saving activity state

当系统,而不是用户关闭一个activity来节约内存时,用户可能希望当他们返回该activity时回到原来的状态。

为了保存activity被kill之前的状态,你可以实现一个onSaveInstanceState()方法。Android在销毁一个activity之前会调用这个函数——也就是说,在onPause()被调用之前。它传递一个Bundle对象给onSaveInstanceState(),在这个对象中你可以使用名字—数值对来记录activity的动态状态。当一个activity再次启动时,该Bundle对象被传递给onCreate()和onRestoreInstanceState()(该方法在onStart()之后调用)。这两个方法可以重新建立被保存的状态。

和onPause()等方法不同,onSaveInstanceState() 和 onRestoreInstanceState() 不是生命周期方法。它们不是总被调用。例如,Android在activity变为可以被系统销毁的状态时调用onSaveInstanceState(),但当该activity被用户销毁时不会调用(例如按下BACK键)。在这种情况下,用户不期望回到该activity,因此不需要保存它的状态。

因为onSaveInstanceState()不是总被调用,你应该只用它来记录activity的瞬时状态,而不是永久性数据。应该用onPause()来完成后者。

Coordinating activities

当一个activity启动另一个时,它们都会经历生命周期的转移。一个会暂停或停止,而另一个会启动。有时候,你需要协调这些activity。 生命周期回调函数的调用顺序是固定的,尤其当两个activity在同一个进程中时:

  1. 当前activity的onPause()方法被调用。
  2. 被启动的activity的onCreate(),onStart()和onResume()依次被调用。
  3. 如果前一个activity不再在屏幕上可见,则它的onStop()方法被调用。

Service lifecycle

一个service可以有两种用法:

  • 它可以被启动并允许一直运行直到有人停止它或者它自行停止。在这种模式下,它以Context.startService()方法来启动并使用Context.StopService()的方法来停止。它可以使用Service.stopSelf()或者Service.stopSelfResult()来停止自己。只需要一次stopService()方法就可以停止该service,无论startService()被调用了多少次。
  • It can be operated programmatically using an interface that it defines and exports. Clients establish a connection to the Service object and use that connection to call into the service. The connection is established by calling Context.bindService(), and is closed by calling Context.unbindService(). Multiple clients can bind to the same service. If the service has not already been launched, bindService() can optionally launch it.
  • 它可以通过它定义的一些接口来程序化的操作。客户和Service对象建立一个连接并使用这个连接来调用service。该连接使用Context.bindService()来建立,并使用Context.unbindService()来断开。很多客户可以绑定同一个service。如果该service没有启动,bindService()可以可选的启动它。

这两种模式并非相互独立。你可以绑定一个由startService()启动的服务。例如,一个后台音乐服务可以由一个指定了需要播放音乐的intent对象使用startService()来启动。只在这以后,可能是当一个用户希望操作播放器或者获取当前歌曲信息,一个activity才会绑定到该service上。在这种情况下,stopService()直到最后一个连接断开才会将service停止。

和activity一样,service也有生命周期方法。但比较少,并且为public而不是protected:

void onCreate()

void onStart(Intent intent)

void onDestroy()

通过实现这些方法,你可以管理service生命周期的两个循环:

  • 完整周期:从onCreate()被调用直到onDestroy()返回。和activity一样,一个service在onCreate()中进行初始化,并在onDestroy()中释放所有资源。例如,一个音乐播放service可以在onCreate()中创建播放音乐的线程,并在onDestroy()中停止该线程。
  • 活跃周期:该周期从onStart()被调用开始。被传给startService()的intent对象被传给该方法,音乐service查看该intent对象来播放其指定的音乐。

        service没有onStop()方法。

onCreate()和onDestroy()方法在所有service中都被调用。无论它们被Context.startService()还是Context.bindService()启动。但onStart()只会为由startService()启动的service调用。

如果一个service允许客户来绑定,有一些其它的方法需要实现:

IBinder onBind(Intent intent)

boolean onUnbind(Intent intent)

void onRebind(Intent intent)

onBind()回调函数的参数intent对象就是bindService()所接受的intent对象,而onUnbind()中的intent对象是unbindService()接受的intent对象。如果该service允许绑定,则onBind()返回用户用来与service交互的信息通道。onUnbind()方法可以要求onRebind()在新的客户连接到service时被调用。  下表列出了一个service的回调函数。虽然它区分了使用startService和bindService启动的service,记住任何service,无论它是怎么启动的,都可以让客户绑定,因此任何service都可以接受onBind()和onUnbind()调用。 

Android SDK 中文文档计划 (0) 我的声明 - [Android SDK 中文文档]

Broadcast receiver lifecycle

一个broadcast receiver只有一个回调方法: void onReceive(Context curContext, Intent broadcastMsg) 当一个关于broadcast receiver的广播消息到达时,android调用它的onReceive()方法并将包含消息的intent对象传递给它。broadcast receiver只在它运行该方法时被认为是活跃的。当onReceive()返回时,它变为非活跃状态。 一个拥有活跃的broadcast receiver的进程被保护不被kill。但是一个只有不活跃的的component的进程可以在任何时候被系统kill掉,当其它进程需要内存时。 这会引入一个问题:当一个广播消息的响应非常耗时间时,需要新建一个新的线程来运行该操作。如果onReceive()创建了这个线程并返回,则整个进程,包括新创建的线程会被认为是非活动的(除非有其它的component为活动的),该线程有可能会被kill掉。解决方案是用onReceive()启动一个service并让该service完成这个工作,那么系统直到这个进程仍然是活跃的。 下一节将进一步讨论进程被kill的可能性。

Processes and lifecycles

Android系统尝试尽可能长的维护一个应用程序进程,但最后它将在内存不足时把老的程序清除掉。为了决定哪些程序该保留、哪些程序应该kill,Android将每个进程放进一个重要性体系中,基于其中运行的component和它们的状态。重要性最低的进程最先被kill掉,以此类推。该体系中有5个等级:

  1. 一个前台进程是用户现在做的事情所需要的进程。一个进程满足下列任一条件即被认为是前台进程:
    • 它正在运行一个正在和用户交互的activity(该Activity对象的onResume()方法被调用)。
    • 它拥有一个被绑定到用户正在交互的activity上的service。
    • 它有一个service对象正在运行onCreate(),onStart()或onDestroy()中的一个。
    • It has a BroadcastReceiver object that's executing its onReceive() method.
    • 他有一个BroadcastReceiver对象正在运行onReceive()方法。
    在某个时间,只有少数的前台进程会存在。它们只有在极端情况下被Kill——内存严重不足以至于它们不能都同时运行。一般的,在这个时间,设备达到了一个请求分页的状态,因此需要kill一些 前台进程来是用户界面能够响应。
  2. 一个可见进程为一个没有任何前台component但仍然能影响用户在屏幕上看到的内容的进程。一个进程满足下列任一条件则被认为是可见进程:
    • 它持有一个activity,该activity不在前台,但仍对用户可见(它的onPause()方法被调用)。例如,一个前台activity为一个对话框,覆盖了后面一个activity。
    • 它持有一个绑定到可见activity的service。
    一个可见进程被认为非常重要,只有在需要资源来让所有前台进程运行时才会被Kill掉。
  3. 一个服务进程是一个运行着使用startService()启动的service但并非前两种类型的进程。虽然服务进程并非直接和用户看见的东西绑定,它们一般来说正在进行用户关心的操作(例如在后台播放音乐或者下载网络数据),因此系统一般会让它们保持运行状态。
  4. 一个后台进程是一个持有一个对用户不可见的activity的进程(Activity的onStop()方法被调用)。这些进程对用户体验没有直接影响,并且可以在任意时间被Kill以保证内存的需要。一般来说有很多后台进程在运行,因此它们被保存在一个LRU列表中来保证最近使用的拥有该activity的进程是最后kill的进程。如果一个activity正确的实现了它的生命周期方法,并保存了它的当前状态,kill该process将不会对用户体验造成不好的影响。
  5. 一个空进程未包含任何活动component的进程。保存这样一个进程的唯一原因就是用来提高下一次运行的速度。为了平衡系统资源,系统常常会清理掉这些进程。

Android将一个进程排在尽可能高的级别,基于其component的重要程度。例如,如果一个进程持有一个service和一个可见的activity,该process将被作为一个可见进程来排序,而不是一个服务进程。 另外,一个进程的排序可能因为其它进程对它的依赖性而上升。一个进程的优先级永远不低于它服务于的进程。例如,如果进程A中的一个content provider服务于进程B中的一个客户,或者如果进程A中的一个service绑定到进程B中的一个component,则A至少和B一样重要。 由于正在运行一个服务的进程比拥有后台activity的进程排序高,因此一个启动一个长期运行的操作的activity可能会启动一个service来做这项工作,而不是启动一个线程——特别是如果该操作会比activity持续的更久的时候。例如播放背景音乐,上传照片等等。使用一个service保证了改操作将至少拥有服务进程的优先级,无论该activity发生了什么。就像前面所说的broadcast receiver生命周期一样,这也是broadcast receiver应该使用service而不是线程来进行耗时操作的原因。