



知识点讲解:光盘:视频\知识点\第4章\进程和线程.avi
进程和线程很容易理解,在PC机的Windows系统中都有一个进程管理器,当打开后,会显示当前运行的所有程序。同样在Android系统中也有进程,当某个组件第一次运行的时候,Android会启动一个进程。在默认情况下,所有的组件和程序运行在这个进程和线程中,也可以安排组件在其他的进程或者线程中运行。本节将详细讲解Android系统中进程和线程的基本知识。
自然界的事物都有自己的生命周期,例如人的生、老、病、死。作为一个Android应用程序也如同自然界的生物一样,有自己的生命周期。我们开发一个程序的目的是为了完成一个功能,例如银行计算加息的软件,每当一个用户去柜台办理取款业务时,银行工作人员便启动了我们这个程序的生命,当用这个软件完成利息计算时,这个软件当前的任务就完成了,此时就需要结束自己的使命。肯定有人提出疑问:生生死死多么麻烦,就让这个程序一直是“活着”的状态,一个用户办理完取款业务后,继续等着下一个用户办理取款业务,这样这个程序就“长生不老”了。其实谁都想自己的程序“长生不老”,但是很不幸,我们不能这样做。原因是计算机的处理性能是一定的,一个人、两个人、三个人计算机可以处理这个任务,但是一个安装这个软件的机器一天会处理上千上万个取款业务,如果它们都一直活着,一台有限配置的计算机能承受得了吗?
由此可见,应用程序的生命周期就是一个程序的存活时间,即在什么时间内有效。Android是一个构建在Linux之上的开源移动开发平台,在Android中,多数情况下每个程序都是在各自独立的Linux进程中运行的。当一个程序或其某些部分被请求时,它的进程就“出生”了;当这个程序没有必要再运行下去且系统需要回收这个进程的内存用于其他程序时,这个进程就“死亡”了。可以看出,Android程序的生命周期是由系统控制而非程序自身直接控制。这和我们编写桌面应用程序时的思维有一些不同,一个桌面应用程序的进程也是在其他进程或用户请求时被创建,但是往往是在程序自身收到关闭请求后执行一个特定的动作(比如从main()函数中返回)而导致进程结束的。要想做好某种类型的程序或者某种平台下的程序的开发,最关键的就是要弄清楚这种类型的程序或整个平台下的程序的一般工作模式并熟记在心。在Android中,程序的生命周期控制就是属于这个范畴。
开发者必须理解不同的应用程序组件,尤其是Activity、Service和Intent Receiver,需要了解这些组件是如何影响应用程序生命周期的。如果不正确地使用这些组件,可能会导致系统终止正在执行重要任务的应用程序进程。
一个常见的进程生命周期漏洞的例子是Intent Receiver(意图接收器),当Intent Receiver在onReceive()方法中接收到一个Intent(意图)时,它会启动一个线程,然后返回。一旦返回,系统将认为Intent Receiver不再处于活动状态,因而Intent Receiver所在的进程也就不再有用了(除非该进程中还有其他的组件处于活动状态)。因此,系统可能会在任意时刻终止该进程以回收占有的内存。这样进程中创建出的那个线程也将被终止。解决这个问题的方法是从Intent Receiver中启动一个服务,让系统知道进程中还有处于活动状态的工作。为了使系统能够正确决定在内存不足时应该终止哪个进程,Android 根据每个进程中运行的组件及组件的状态把进程放入一个Importance Hierarchy(重要性分级)中。
组件运行的进程是由manifest file控制的。组件的节点一般都包含一个process属性,例如<activity>、<service>、<receiver>和<provider>节点。属性process可以设置组件运行的进程,可以配置组件在一个独立进程中运行,或者多个组件在同一个进程中运行,甚至可以多个程序在一个进程中运行,当然前提是这些程序共享一个User ID并给定同样的权限。另外<application>节点也包含了process属性,用来设置程序中所有组件的默认进程。
当更加常用的进程无法获取足够内存时,Android会智能地关闭不常用的进程,当下次启动程序时会重新启动这些进程。当决定哪个进程需要被关闭的时候,Android会考虑哪个对用户更加有用。例如Android会倾向于关闭一个长期不显示在界面的进程来支持一个经常显示在界面的进程。是否关闭一个进程决定于组件在进程中的状态。
进程的类型多种多样,按照重要的程度主要包括如下几类进程。
(1)前台进程(Foreground)
前台进程是看得见的,与用户当前正在做的事情密切相关,不同的应用程序组件能够通过不同的方法将它的宿主进程移到前台。在如下的任何一个条件下系统会把进程移动到前台。
(2)可见进程(Visible)
可见进程也是可见的,它有一个可以被用户从屏幕上看到的活动,但不在前台(它的onPause()方法被调用)。假如前台的活动是一个对话框,以前的活动隐藏在对话框之后就会出现这种进程。可见进程非常重要,一般不允许被终止,除非是为了保证前台进程的运行而不得不终止它。
(3)服务进程(Service)
服务进程是无法看见的,拥有一个已经用startService()方法启动的服务。虽然用户无法直接看到这些进程,但它们做的事情却是用户所关心的(如后台MP3回放或后台网络数据的上传下载)。所以系统将一直运行这些进程,除非内存不足以维持所有的前台进程和可见进程。
(4)后台进程(Background)
后台进程也是看不见的,只有打开之后才能看见。例如迅雷下载,我们可以将其最小化,虽然在桌面上看不见了,但是它一直在进行下载的工作,拥有一个当前用户看不到的活动(它的onStop()方法被调用)。这些进程对用户体验没有直接的影响。如果它们正确执行了活动生命周期,系统可以在任意时刻终止该进程以回收内存,并提供给前面3种类型的进程使用。系统中通常有很多这样的进程在运行,因此要将这些进程保存在LRU列表中,以确保当内存不足时用户最近看到的进程最后一个被终止。
(5)空进程(Empty)
空进程是指不拥有任何活动的应用程序组件的进程。保留这种进程的唯一原因是在下次应用程序的某个组件需要运行时,不需要重新创建进程,这样可以提高启动速度。系统将以进程中当前处于活动状态组件的重要程度为基础对进程进行分类。进程的优先级可能也会根据该进程与其他进程的依赖关系而增长。假如进程A通过在进程B中设置Context.BIND_AUTO_CREATE标记或使用ContentProvider被绑定到一个服务(Service),那么进程B在分类时至少要被看成与进程A同等重要。
例如Activity的状态转换图如图4-3所示。
图4-3 Activity状态转换图
图4-3所示的状态的变化是由Android内存管理器决定的,Android会首先关闭那些包含Inactive Activity的应用程序,然后关闭Stopped状态的程序。只有在极端情况下才会移除Paused状态的程序。
当用户界面需要很快对用户进行响应时,就需要将一些费时的操作,如网络连接、下载或者非常占用服务器时间的操作等放到其他线程。也就是说,即使为组件分配了不同的进程,有时也需要再分配线程。
线程是通过Java的标准对象Thread来创建的,在Android中提供了如下方便的管理线程的方法。