线程安全方法(Thread-safe methods)
在一些情况下,你所实现的方法有可能会被多于一个的线程所调用,所以它们必须被写成线程安全的。
对于我们上一节所讨论的RPC机制中的可以被远程调用的方法来说,这是必须首先考虑的。如果针对一个IBinder对象中实现的方法的调用源自这个 IBinder对象所在的进程时,这个方法将会在调用者的线程中执行。然而,如果这个调用源自其它的进程,则这个方法将会在一个线程池中选出的线程中运 行,这个线程池由Android加以管理,并与IBinder存在于同一进程内;这个方法不会在进程的主线程内执行。反过来说,一个服务的 onBind() 方法应为服务进程的主线程所调用,而实现了由 onBind() 返回的对象(比如说,一个实现了RPC方法的Stub的子类)的方法将为池中的线程所调用。因为服务可以拥有多于一个的客户端,而同一时间,也会有多个池中的线程调用同一个IBinder方法。因此IBinder方法必须实现为线程安全的。
类似的,一个内容提供者能接受源自其它进程的请求数据。尽管ContentResolver和ContentProvider类隐藏了交互沟通过程的管理细节,ContentProvider会由query()
, insert()
, delete()
, update()
和getType()
方法来相应这些请求,而这些方法也都是由那个内容提供者的进程中所包涵的线程池提供的,而不是进程的主线程本身。所以这些有可能在同一时间被很多线程调用的方法也必须被实现为线程安全的。
组件生命周期(Component Lifecycles)
应用程序组件有其生命周期──由Android初始化它们以相应intent直到这个实例被摧毁。在此之间,它们有时是激活的有时则相反。或者,如果它是 一个activity,则是可为用户所见或者不能。这一节讨论了activity、服务以及广播接收器的生命周期,包括它们在生命周期中的状态、在状态之 间转变时通知你的方法、以及当这些进程被关闭或实例被摧毁时,这些状态产生的效果。
Activity生命周期(Activity lifecycle)
一个activity主要有三个状态:
* 当在屏幕前台时(位于当前任务堆栈的顶部),它是活跃或运行的状态。它就是相应用户操作的activity。
* 当它失去焦点但仍然对用户可见时,它处于暂停状态。即是:在它之上有另外一个activity。这个activity也许是透明的,或者未能完全遮蔽全屏,所以被暂停的activity仍对用户可见。暂停的activity仍然是存活状态(它保留着所有的状态和成员信息并连接至窗口管理器),但当系统处于极低内存的情况下,仍然可以杀死这个activity。
* 如果它完全被另一个activity覆盖是,它处于停止状态。它仍然保留所有的状态和成员信息。然而它不在为用户可见,所以它的窗口将被隐藏,如果其它地方需要内存,则系统经常会杀死这个activity。
如果一个activity处于暂停或停止状态,系统可以通过要求它结束(调用它的 finish() 方法)或直接杀死它的进程来将它驱出内存。当它再次为用户可见的时候,它只能完全重新启动并恢复至以前的状态。
当一个activity从这个状态转变到另一个状态时,它被以下列protected方法所通知:
void onCreate(Bundle savedInstanceState)
void onStart()
void onRestart()
void onResume()
void onPause()
void onStop()
void onDestroy()
你可以重载所有这些方法以在状态改变时进行合适的工作。所有的activity都必须实现 onCreate()
用以当对象第一次实例化时进行初始化设置。很多activity会实现 onPause()
以提交数据变化或准备停止与用户的交互。
调用父类(Calling into the superclass)
所有activity生命周期方法的实现都必须先调用其父类的版本。比如说:
总得来说,这七个方法定义了一个activity完整的生命周期。实现这些方法可以帮助你监察三个嵌套的生命周期循环:
* 一个activity 完整的生命周期 自第一次调用 onCreate()开始,直至调用onDestroy()为止。activity在onCreate()中设置所有“全局”状态以完成初始化,而在onDestroy()中释放所有系统资源。比如说,如果activity有一个线程在后台运行以从网络上下载数据,它会以 onCreate()创建那个线程,而以
onDestroy()销毁那个线程。
* 一个activity的 可视生命周期自 onStart() 调用开始直到相应的 onStop()调用。在此期间,用户可以在屏幕上看到此activity,尽管它也许并不是位于前台或者正在与用户做交互。在这两个方法中,你可以管控用来向用户显示这个activity的资源。比如说,你可以在onStart() 中注册一个BroadcastReceiver 来监控会影响到你UI的改变,而在onStop() 中来取消注册,这时用户是无法看到你的程序显示的内容的。onStart() 和 onStop() 方法可以随着应用程序是否为用户可见而被多次调用。
* 一个activity的 前台生命周期 自 onResume() 调用起,至相应的 onPause()调用为止。在此期间,activity位于前台最上面并与用户进行交互。activity会经常在暂停和恢复之间进行状态转换──比如说当设备转入休眠状态或有新的activity启动时,将调用onPause() 方法。当activity获得结果或者接收到新的intent的时候会调用onResume() 方法。因此,在这两个方法中的代码应当是轻量级的。
下图展示了上述循环过程以及activity在这个过程之中历经的状态改变。着色的椭圆是activity可以经历的主要状态。矩形框代表了当activity在状态间发生改变的时候,你进行操作所要实现的回调方法。
下表详细描述了这些方法,并在activity的整个生命周期中定位了它们。
方法 |
描述 |
是否可被杀死(Killable?) |
下一个 |
在activity第一次被创建的时候调用。这里是你做所有初始化设置的地方──创建视图、绑定数据至列表等。如果曾经有状态记录(参阅后述Saving Activity State。),则调用此方法时会传入一个包含着此activity以前状态的包对象做为参数。 接下来始终遵循调用onStart()。 |
否 |
onStart() |
|
在activity停止后,在再次启动之前被调用。 接下来始终遵循调用onStart()。 |
否 |
onStart() |
|
当activity正要变得为用户所见时被调用。 当activity转向前台时接下来调用onResume(),在activity变为隐藏时接下来调用onStop()。 |
否 |
onResume() 或 onStop() |
|
在activity开始与用户进行交互之前被调用。此时activity位于堆栈顶部,并接受用户输入。 接下来始终遵循调用onPause()。 |
否 |
|
|
当系统将要启动另一个activity时调用。此方法主要用来将未保存的变化进行持久化,停止类似动画这样耗费CPU的动作等。这一切动作应该在短时间内完成,因为下一个activity必须等到此方法返回后才会继续。 当activity重新回到前台时接下来调用onResume()。当activity变为用户不可见时接下来调用onStop()。 |
是 |
onResume() 或 onStop() |
|
当activity不再为用户可见时调用此方法。这可能发生在它被销毁或者另一个activity(可能是现存的或者是新的)回到运行状态并覆盖了它。 如果activity再次回到前台跟用户交互则接下来调用onRestart(),如果关闭activity则接下来调用onDestroy()。 |
是 |
onRestart() or onDestroy() |
|
在activity销毁前调用。这是activity接收的最后一个调用。这可能发生在activity结束(调用了它的 |
是 |
无 |
请注意上表中可被杀死一列。它标示了在方法返回后,还没执行activity的其余代码的任意时间里,系统是否可以杀死包含此activity的进程。三个方法(onPause()、 onStop()和
onDestroy())被标记为“是”。onPause()是三个中的第一个,它也是唯一一个在进程被杀死之前必然会调用的方法──onStop() 和 onDestroy() 有可能不被执行。因此你应该用 onPause() 来将所有持久性数据(比如用户的编辑结果)写入存储之中。
在可被杀死一列中标记为“否”的方法在它们被调用时将保护activity所在的进程不会被杀死。所以只有在onPause()方法返回后到
onResume() 方法被调用时,一个activity才处于可被杀死的状态。在onPause()再次被调用并返回之前,它不会被系统杀死。
如后面一节进程和生命周期所述,即使是在这里技术上没有被定义为“可杀死”的activity仍然有可能被系统杀死──但这仅会发生在实在没有其它方法的极端情况之下。
保存activity状态(Saving activity state)
当系统而不是用户自己出于回收内存的考虑,关闭了一个activity之后。用户会期望当他再次回到那个activity的时候,它仍保持着上次离开时的样子。
为了获取activity被杀死前的状态,你应该为activity实现onSaveInstanceState()
方法。Android在activity有可能被销毁之前(即onPause() 调用之前)会调用此方法。它会将一个以名称-值对方式记录了activity动态状态的Bundle 对象传递给该方法。当activity再次启动时,这个Bundle会传递给onCreate()方法和随着onStart()方法调用的onRestoreInstanceState()
,所以它们两个都可以恢复捕获的状态。
与onPause()或先前讨论的其它方法不同,onSaveInstanceState() 和 onRestoreInstanceState() 并不是生命周期方法。它们并不是总会被调用。比如说,Android会在activity易于被系统销毁之前调用 onSaveInstanceState(),但用户动作(比如按下了BACK键)造成的销毁则不调用。在这种情况下,用户没打算再次回到这个activity,所以没有保存状态的必要。
因为onSaveInstanceState()不是总被调用,所以你应该只用它来为activity保存一些临时的状态,而不能用来保存持久性数据。而是应该用onPause()来达到这个目的。
服务生命周期(Coordinating activities)
服务以两种方式使用:
* 它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()
启动,而以调用Context.stopService()
结束。它可以调用Service.stopSelf()
或 Service.stopSelfResult()
来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。
* 它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()
方法建立,以调用 Context.unbindService()
关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
这两种模式并不是完全分离的。你可以绑定至一个用 startService()启动的服务。比如说,一个后台音乐播放服务可以调用startService()并传递给它一个包含欲播放的音乐列表的Intent对象来启动。不久,当用户想要对播放器进行控制或者查看当前播放曲目的详情时,会启用一个activity,调用bindService()连接到服务来完成操作。在这种情况下,直到绑定连接关闭stopService() 才会真正停止一个服务。
与activity一样,服务也有一系列你可以实现以用于监控其状态变化的生命周期方法。但相对于activity要少一些,只有三个,而且,它们是public属性,并非protected:
void onCreate()
void onStart(Intent intent)
void onDestroy()
倚仗实现这些方法,你监控服务的两个嵌套的生命周期循环:
* 服务的完整生命周期始于调用onCreate()
而终于onDestroy()
方法返回。如同activity一样,服务在onCreate()里面进行它自己的初始化,而在onDestroy()里面释放所有资源。比如说,一个音乐回放服务可以在onCreate()中创建播放音乐的线程, 而在onDestroy()中停止这个线程。
* 服务的活跃生命周期始于调用onStart()
。这个方法用于处理传递给startService()的Intent对象。音乐服务会打开Intent来探明将要播放哪首音乐,并开始播放。
服务停止时没有相应的回调方法──不存在onStop()方法。
onCreate()和onDestroy()方法在所有服务中都会被调用,无论它们是由Context.startService()
还是由Context.bindService()
所启动的。而onStart()仅会被startService()所启用的服务调用。
如果一个服务允许别的进程绑定,则它还会有以下额外的回调方法以供实现:
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
传递给bindService的Intent的对象也会传递给onBind()
回调方法,而传递给unbindService()的Intent对象同样传递给onUnbind()
。如果服务允许绑定,onBind()将返回一个供客户端与服务进行交互的通讯渠道。如果有新的客户端连接至服务,则onUnbind()方法可以要求调用onRebind()
。
下图描绘了服务的回调方法。尽管图中对由startService 和startService方法启动的服务做了区分,但要记住,不论一个服务是怎么启动的,它都可能允许客户端的连接,所以任何服务都可以接受onBind()和onUnbind()调用。
广播接收器生命周期(Broadcast receiver lifecycle)
广播接收器只有一个回调方法:
void onReceive(Context curContext, Intent broadcastMsg)
当广播消息抵达接收器时,Android调用它的onReceive()
方法并将包含消息的Intent对象传递给它。广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态。
拥有一个活跃状态的广播接收器的进程被保护起来而不会被杀死。但仅拥有失活状态组件的进程则会在其它进程需要它所占有的内存的时候随时被杀掉。
这种方式引出了一个问题:如果响应一个广播信息需要很长的一段时间,我们一般会将其纳入一个衍生的线程中去完成,而不是在主线程内完成它,从而保证用户交互过程的流畅。如果onReceive()衍生了一个线程并且返回,则包涵新线程在内的整个进程都被会判为失活状态(除非进程内的其它应用程序组件仍处于活跃状态),于是它就有可能被杀掉。这个问题的解决方法是令onReceive()启动一个新服务,并用其完成任务,于是系统就会知道进程中仍然在处理着工作。
下一节中,我们会讨论更多进程易误杀的问题。
进程与生命周期(Processes and lifecycles)
Android系统会尽可能长的延续一个应用程序进程,但在内存过低的时候,仍然会不可避免需要移除旧的进程。为决定保留或移除一个进程,Android 将每个进程都放入一个“重要性层次”中,依据则是它其中运行着的组件及其状态。重要性最低的进程首先被消灭,然后是较低的,依此类推。重要性共分五层,依 据重要性列表如下:
1. 前台进程是用户操作所必须的。当满足如下任一条件时,进程被认为是处于前台的:
* 它运行着正在与用户交互的activity(Activity对象的 onResume()
方法已被调用)。
* 一个正在与用户交互的activity使用着它提供的一个服务。
* 它包含着一个正在执行生命周期回调方法(onCreate()
、onStart()
或onDestroy()
)的Service对象。
* 它包含着一个正在执行 onReceive()
方法的BroadcastReceiver对象。
任一时间下,仅有少数进程会处于前台,仅当内存实在无法供给它们维持同时运行时才会被杀死。一般来说,在这种情况下,设备已然处于使用虚拟内存的状态,必须要杀死一些前台进程以用户界面保持响应。
2. 可视进程没有前台组件,但仍可被用户在屏幕上所见。当满足如下任一条件时,进程被认为是可视的:
* 它包含着一个不在前台,但仍然为用户可见的activity(它的onPause()
方法被调用)。这种情况可能出现在以下情况:比如说,前台activity是一个对话框,而之前的activity位于其下并可以看到。
* 它包含了一个绑定至一个可视的activity的服务。
可视进程依然被视为是很重要的,非到不杀死它们便无法维持前台进程运行时,才会被杀死。
3. 服务进程是由 startService()
方法启动的服务,它不会变成上述两类。尽管服务进程不会直接为用户所见,但它们一般都在做着用户所关心的事情(比如在后台播放mp3或者从网上下载东西)。所以系统会尽量维持它们的运行,除非系统内存不足以维持前台进程和可视进程的运行需要。
4. 背景进程包含目前不为用户所见的activity(Activity对象的 onStop()
方法已被调用)。这些进程与用户体验没有直接的联系,可以在任意时间被杀死以回收内存供前台进程、可视进程以及服务进程使用。一般来说,会有很多背景进程 运行,所以它们一般存放于一个LRU(最后使用)列表中以确保最后被用户使用的activity最后被杀死。如果一个activity正确的实现了生命周 期方法,并捕获了正确的状态,则杀死它的进程对用户体验不会有任何不良影响。
5. 空进程不包含任何活动应用程序组件。这种进程存在的唯一原因是做为缓存以改善组件再次于其中运行时的启动时间。系统经常会杀死这种进程以保持进程缓存和系统内核缓存之间的平衡。
Android会依据进程中当前活跃组件的重要程度来尽可能高的估量一个进程的级别。比如说,如果一个进程中同时有一个服务和一个可视的activity,则进程会被判定为可视进程,而不是服务进程。
此外,一个进程的级别可能会由于其它进程依赖于它而升高。一个为其它进程提供服务的进程级别永远高于使用它服务的进程。比如说,如果A进程中的内容提供者 为进程B中的客户端提供服务,或进程A中的服务为进程B中的组件所绑定,则A进程最低也会被视为与进程B拥有同样的重要性。
为运行着一个服务的进程重要级别总高于一个背景activity。所以一个activity以启动一个服务的方式启动一个长时间运行过程比简单的衍生一个 线程来进行处理要好。尤其是当处理过程比activity本身存在时间要长的情况之下。我们以背景音乐播放和上传一个相机拍摄的图片至网站上为例。使用服 务则不论activity发生何事,都至少可以保证操作拥有“服务进程”的权限。如上一节广播接收器生命周期 所提到的,这也正是广播接收器使用服务,而不是使用线程来处理耗时任务的原因。