进程和线程
间通信
当应用程序组件启动,应用程序没有任何其他组件上运行,Android系统启动与执行单线程应用一个新的Linux进程。缺省情况下,同一应用程序的所有组件在同一进程和线程(称为“主”线程)运行。如果一个应用程序组件开始,已经存在该应用程序的处理(因为从应用另一种组分存在),则该组件被该过程中开始,并使用执行相同的线程。但是,您可以安排您的应用程序在单独的进程中运行不同的组件,并且您可以为任意程序创建额外的线程。
本文讨论进程和线程如何在Android应用程序。
进程
缺省情况下,同一应用程序的所有组件在同一进程中运行,并且大多数应用程序不应该改变这种情况。但是,如果你发现你需要控制其处理某个部件属于,你可以在清单文件中这样做。
对于每种类型的组件元素 - <活动>,<服务>,<接收器>和<提供商>的清单条目 - 支持一个机器人:可指定在该组件应该运行的过程的过程属性。使每个组件在自己的进程中运行,或使一些组件共享一个进程,而别人没有,你可以设置该属性。还可以设置机器人:过程使得不同的应用程序组件在运行相同过程提供的应用程序共享相同的Linux用户ID和相同的证书签名。
在<application>元素也支持一个机器人:process属性,设置适用于所有组件的默认值。
机器人可能决定在某一时刻关闭的过程中,当存储器是低并要求被更直接服务于用户的其它过程。在这个过程中运行的应用程序丧生组件都因此被毁。一个进程再次启动的时候再有工作给他们做这些组件。
在决定哪些进程杀掉,Android系统的重量为他们的用户相对重要性。例如,它更容易关闭托管不再显示在屏幕上,相比于托管可见活动的处理活动的方法。是否要终止一个进程的决定,因此,依赖于在该进程中运行的组件的状态。用于决定哪些进程终止规则将在下面讨论。
流程生命周期
Android系统试图维持一个应用程序,只要有可能,但最终需要删除旧的流程,以回收内存为新的或更重要的进程。以确定哪些进程保持和杀,系统中的每个过程放置到基于在过程和这些组件的运行状态的部件的“重要性的层次结构”。具有最低重要性过程首先消除,那么那些具有下一个最低重要性等,在必要时恢复系统资源。
中有重要的层次结构五个等级。下面列出了不同类型的进程按重要性顺序(第一个过程是最重要和最被杀害最后一个):
前台进程
所需什么用户目前正在做的方法。进程被认为是如果任一下列条件,则前景:
它承载了用户与交互的活动(Activity的onResume()方法被调用)。
它承载的绑定到用户交互的活动相关的服务。
它承载的“在前台”运行服务-the服务已呼吁startForeground()。
它承载的执行其生命周期回调的一个服务(的onCreate(),在onStart(),或的onDestroy())。
它承载的执行它的onReceive()方法一个BroadcastReceiver。
通常,只有几前台进程在任何给定时间存在。他们被杀害只能作为最后的手段,如果内存是如此之低,他们不可能全部继续运行。通常,在这一点上,该装置已经达到了一个内存分页的状态,所以杀死某些前台进程需要保持用户界面的响应。
可见过程
不具有任何前台组件,但仍的方法,可能会影响用户看到在屏幕上的内容。进程被认为是如果以下任一条件,则可见:
它承载的活动不是在前台,但仍是可见的用户(其的onPause()方法被调用)。这可能会发生,例如,如果前景活动开始对话,这使得它的后面被视为以前的活性。
它承载绑定到一个可见的(或前景)活动相关的服务。
一个可视进程被认为是极其重要的,除非这样做是需要保持所有前台进程运行,就不会被撞死。
服务流程
运行已开始与startService()方法和不归类于两个较高类别的服务的过程。虽然服务过程不直接依赖于任何用户看到的,他们一般做的事情,用户关心的(如播放的背景音乐,或在网络上下载数据),这样系统保持运行它们,除非有没有足够的内存留住他们连同所有前台和可视进程。
后台进程
一个过程拿着一个活动,这不是当前对用户可见(活动的的onStop()方法被调用)。这些方法具有对用户体验没有直接的影响,并且该系统可以在任何时候杀死它们回收内存为前景,可见光或服务的过程。通常有许多运行后台进程,因此它们被保存在一个LRU(最近最少使用)列表,以确保与最近一次被用户看到该活动的过程是最后被杀害。如果一个activity正确的实现它的生命周期方法,并保存其当前状态,杀死它的进程不会对用户体验明显的效果,因为当用户返回到活动,该活动将恢复其所有可见的状态。请参阅有关保存和恢复状态信息的活动文档。
空进程
不持有任何活动应用组件的过程。保持这种进程活着的唯一原因是为了缓存目的,下一次组件需要在运行它以提高启动时间。该系统往往为了处理高速缓存和底层的内核缓存之间平衡整个系统的资源,杀死这些进程。
机器人排在它可以最高水平的方法,基于目前的过程中活性组分的重要性。例如,如果一个进程承载服务和可见光活性,该过程被列为一个可见的过程,而不是一个服务进程。
另外,一个过程的排名将会增加,因为其它工艺依赖于它-一个正服务另一个进程处理永远不能被排名比它所服务的进程低。例如,如果在处理A的内容提供者的服务中处理B的客户机,或者如果在过程A中的服务绑定到在过程B中的成分,处理A始终被认为至少进程B一样重要
由于运行的服务的进程比后台活动的过程中,启动一个长期运行的操作可能做得很好,该操作启动一个服务,而不是简单地创建一个工作线程,特别是如果操作将可能的活动排名较高经久活动。例如,这是上载的图像到一个网站的活性应该启动一个服务来执行上载,以便上载可以在后台继续即使用户离开活性。使用服务保证了操作具有至少“服务进程”优先,不管发生什么情况的活性。这是广播接收器应该使用服务,而不是简单地把费时的操作在一个线程一样的道理。
主题
当一个应用程序启动时,系统会创建执行应用程序的线程,称为“主”。此线是非常重要的,因为它是负责调度事件到相应的用户界面部件,包括绘图事件。也正是在您的应用程序从Android的UI工具包(从android.widget和android.view封装组件)组件交互的线程。这样,主线程有时也被称为UI线程。
系统不会为组件的每个实例创建一个单独的线程。在同一进程中运行的所有组件被实例化在UI线程,和系统调用每个部件都从该线程调度。因此,该系统响应回调方法(如的onkeydown()报告用户操作或一个生命周期回调方法)的过程中的UI线程始终运行。
例如,当用户触摸屏幕上的按钮,应用的UI线程调度触摸事件的部件,这反过来将它的按压状态和支柱的无效请求到事件队列中。在UI线程取消请求排队,并通知应重绘自己的小部件。
当你的应用程序中执行响应用户交互密集的工作,这个单一线程模型可以产生除非你正确地执行你的应用性能不佳。特别是,如果一切都在UI线程中发生的事情,执行长时间的操作,如网络访问或数据库查询将阻止整个UI。当线程被阻塞,任何事件可以被调度,包括绘图事件。从用户的角度看,应用程序似乎挂起。更糟的是,如果UI线程被阻塞超过几秒钟(目前大约5秒)的用户呈现臭名昭著的“应用程序没有响应”(ANR)对话。然后,用户可以决定退出应用程序,并卸载它,如果他们不高兴。
此外,Andoid UI工具包不是线程安全的。所以,你不能操纵从一个工人的UI线程,你必须做的所有操作,以从UI线程的用户界面。因此,仅仅是两个规则Android的单线程模型:
不要阻塞UI线程
不要从UI线程外部访问Android的UI工具包
工作线程
由于上述的单线程模型,这是你的应用程序的UI,你不阻塞UI线程响应是至关重要的。如果你有操作执行无法瞬间,你应该确保做他们在单独的线程(“背景”或“工人”线程)。
例如,下面是对从一个单独的线程下载的图像,并显示在一个ImageView的一个点击监听一些代码:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
起初,这似乎做工精细,因为它创造了一个新的线程来处理网络操作。但是,它违反了单线程模型的第二条规则:不要从UI线程此示例外部访问Android的UI工具包修改从工作线程,而不是UI线程的ImageView的。这可能导致未定义和意外的行为,这可能是困难和耗时的追查。
要解决这个问题,Android提供了几种方法可以从其他线程访问UI线程。这里有方法可以帮助一个列表:
Activity.runOnUiThread(可运行)
View.post(可运行)
View.postDelayed(Runnable接口,长)
例如,您可以通过使用View.post(Runnable接口)方法解决上面的代码:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap bitmap =
loadImageFromNetwork("http://example.com/image.png");
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
}
现在这个实现是线程安全的:网络运行是由一个单独的线程完成,而ImageView的是从UI线程操作。
然而,由于操作的复杂性的增长,这种代码可以得到复杂和难以维护。为了处理一个工作线程更复杂的交互,你可能会考虑在你的工作线程使用一个处理程序,处理来自UI线程传递的消息。也许是最好的解决方案,不过,是延长的AsyncTask类,它简化了需要与UI交互工作线程任务的执行。
使用AsyncTask的
AsyncTask的允许您在用户界面上执行异步工作。它在辅助线程执行阻塞操作,然后发布在UI线程上的结果,而不需要你来处理线程和/或处理自己。
要使用它,你必须继承的AsyncTask并实现doInBackground()回调方法,它运行在后台线程池。要更新你的UI,你应该实现onPostExecute(),它从doInBackground传递()的结果并运行在UI线程,所以你可以安全地更新UI。然后,您可以通过运行调用execute()从UI线程的任务。
例如,您可以使用AsyncTask的这种方式实现了前面的例子:
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
现在UI是安全的代码更简单,因为它工作纳入应在一个工作线程,并应在UI线程上执行的一部分来完成的部分分离出来。
你应该阅读有关如何使用这个类有充分的认识的AsyncTask的参考,但这里是它如何工作的简要概述:
可以指定参数的类型,进展值,任务的最终值,使用泛型
该方法doInBackground()在工作线程自动执行
onPreExecute(),onPostExecute()和onProgressUpdate()都调用UI线程上
由doInBackground()的返回值发送到onPostExecute()
您可以拨打publishProgress()在任何时候在doInBackground()到UI线程上执行onProgressUpdate()
您可以随时取消任务,从任何线程
注意:还有一个问题使用工作线程时,在你的活动由于运行时配置更改意外重启(当用户更改屏幕方向,如),这可能会破坏你的工作线程可能会遇到的。就看你怎么可以在这些重启之一,如何正确地取消当活动被销毁任务。在坚持你的任务,请参阅该货架示例应用程序的源代码。
线程安全的方法
在某些情况下,你实现的方法可以从多个线程调用,因此必须被写入到是线程安全的。
这主要是如此,可以调用远程 - 如在绑定服务方法的方法。当在一个IBinder实现的方法的呼叫在将的IBinder运行相同过程开始,该方法在呼叫方的线程中执行。然而,当呼叫在另一个进程发起,该方法在从线程池,该系统中的相同的进程中的IBinder保持(它不是在该过程的UI线程执行)选择的线程中执行。例如,而一个服务的onBind()方法将被从该服务的过程的UI线程调用时,在该onBind()返回(例如,一个实现RPC方法的子类)将被从线程调用的对象实现的方法池。因为一个服务可以具有一个以上的客户机,一个以上的池螺纹可接合在同一时间在同一的IBinder方法。的IBinder方法,因此必须实施是线程安全的。
类似地,内容提供者可以接收源自其他过程数据的请求。虽然ContentResolver的和ContentProvider的类隐藏的进程间通信的管理方式,ContentProvider的方法,为那些响应的细节要求,方法查询(),插入(),删除(),更新()和的getType() - 被称为从线程在内容提供者的处理的池,而不是为过程UI线程。因为这些方法可能会从任意数量的同时线程被调用,它们也必须被实现为线程安全的。
间通信
Android提供了用于使用远程过程调用(RPC),其中一个方法是通过一个活动或其它应用程序组件调用进程间通信(IPC)的机构,但在远程执行(在另一个过程),与任何结果返回给调用者。这需要分解的方法调用和它的数据到一个级别的操作系统能理解,从本地进程和地址空间发送它到远程进程和地址空间,然后重新组装和重演呼叫那里。返回值然后在相反的方向传送。 Android提供所有的代码来执行这些IPC交易,这样你就可以专注于定义和实施的RPC编程接口。
要执行工控机,应用程序必须()绑定到一个服务,使用bindService。欲了解更多信息,请参阅服务开发者指南。