
Android笔记--Processes and Threads
Processes and Threads
默认情况下,一个app的所有components在一个process下的同一个thread下运行(main thread)。当一个components启动时,如果app已经有一个process(其他components已经启动),这个components在这个process内启动,使用same thread。然而你可以arrange你的app在独立的process,并且你可以创建additional threads for any process
Processes
mainifest 中为每一种components(“activity, service, receiver, provider”)提供android:process 属性可以指定components要运行的所属的process。你可以控制每一个component运行在自己的process或者一些components share a process。你可以设置android:process不同的app的components运行在同一个process。
*
Android可能在内存低时shutdown一个process。 自然隶属于这个process的components也destroyed。需要时再次启动。
Process lifecycle
Android System place 每一个process into an importance hierarchy,资源不足时lowest importance将被kill。 importtance hierarchy中有5个levels, 从高到低如下所示:
- Foreground process
与用户当前操作相关的process
- 正在交互的Activity(Activity’s onResume()被调用)
- 绑定在正在交互的Acitity的Service
- Service that running in the foreground—service调用startForeground()
- 正在执行生命周期的回调的Service—(onCreate, onStart, onDestroy)
- 正在执行onReceive的BroadcastReceiver 通常只有少数Foreground process。只有当系统内存低到不能运行时,才会kill他们。通常这个时候,设备已经到了一个memory paging state(内存分页状态?),因此杀死一些Foreground Process以保证用户界面的响应。
- Visible process
虽然没有Foreground components,但是仍然可以影响用户可见的process。
- onPsuse被调用的Activity
- 绑定到visible or foreground activity的Service visible process通常也很重要,除非只有这么做才能保证foreground process正常运行时,才这么做。
- Service process 由startService启动的,并且不属于前面2种的。 尽管service processes 不直接与用户界面相关,但是他们通常做着用户关心的事情,如后台音乐播放或者网络数据下载。除非为了保证前两种的正常运行才会kill他。
- Background process 用户不可见的进程(onStop被调用)。这类进程和用户体验无直接联系,系统随时kill以reclaim memory。通常有很多Background process,因此他们在一个LRU(least recently used)队列。如果activity正确的实现了他的生命周期,并保存它当前的状态,kill这类进程不会对用户体验有影响,因为当用户回到这个activity时,会恢复他之前的状态。详见Actitiies文档关于保存恢复状态的描述。
- Emtpy process 没有任何active application component的进程。这种进程的存在只有一个原因:为增快下次启动component的时间。例如,一个content provider A 正在为process B服务,或者一个service A 绑定到process B,A总是比B低优先级。 由于运行中的service进程总是比后台的activity进程优先级高,因此一个activity中的长时间的操作最好是起一个service。比如,一个上传图片到网络的activity,最好是起一个service来执行上传,这样即使用户离开actitivy,上传工作还能在后台继续。Service可以保证不敢activity发生什么,至少有一个service process的优先级。 broadcast receivers should employ services而不是put time-consuming operations in a thread也是同样的原因。
Threads
当app启动,系统创建一个thread来执行,称作main。 这个线程很重要,负责用户界面的事件调度,包括绘制事件。因此,main thread有时被称作UI thread
系统并不会为每一个compnent的instance创建一个独立的thread。同一个进程下的所有的components在UI thread中被instantiated,对于各个component的系统调用也从UI thread dispatch。相应的,method that response to system callback(onKeyDown)或者生命周期的回调也在UI线程中运作。
例如,当用户touch a button,UI线程dispatch触摸事件给widget,这个widget设置pressed status,并post 一个invalidate request给event queue。然后,UI线程dequeues the request并且notify widget重绘。
当你的app为用户交互执行密集型的工作时,这个thread会表现很差。富国每一个UI时间,都要一个很长时间的操作(网络访问,数据库查询等),整个UI可能阻塞。这时,没有事件能被dispatch,包括绘制时间按。对用户来说就是程序没有响应。如果阻塞超过一定时间(现在是5秒)会出现程序无响应的对话框。
另外,UI toolkit 不是线程安全的。因此,不要从worker thread操作UI线程—你对用户界面的操作,必须全部在UI线程中。这里有2个原则。
- Do not block UI the thread
- Do not access Android UI toolkit from outside the UI thread
Worker threads
一下是一个例子,一个click listenter下载一个图片,显示在ImageView中
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();
}
看起来work fine,因为他创建了一个新的线程去处理网络操作。然而,他违反了第二条规则:不要在UI线程外访问UI toolkit,可能会引发不确定的意外的行为,并且很难跟踪。
因此,android提供几个从其他线程访问UI线程的方法。
- Actitity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
上面的例子可以通过View.post修正
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();
}
现在的实现才是线程安全的。 然而,当操作的复杂度增长,这种代码可读性会很差,很难维护。worker线程处理更复杂的交互时,你可以考虑使用Handler来处理UI线程分发的消息。但是,最佳解决方案可能还是集成AsyncTask类,它简化了需要与UI交互的worker线程的执行。
Using AsyncTask
AsynTask提供了与界面的异步交互。他在worker线程提供一个阻塞的操作,然后在UI线程publish结果,你不要自己来处理threads或者handlers。 使用方法:集成AsyncTask实现doInbackground回调函数,它运行在一个后台运行的线程池。为了更新UI,你需要实现onPostExecute,它取得doInBackground的结果并在UI线程中处理。 你只需要通过调用在UI线程调用execute即可。
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安全,并且代码简洁。AsyncTask的工作方式:
- 你需要制定参数的类型,处理的值的类型,和最终返回值的类型,通过模板
- doInBackground自动在worker线程执行
- onPreExecute, onPostExecute, onProgressUpdate在UI线程中被调用
- doInBackground的返回被发送给onPostExecute
- 你在可以doInBackground中调用publishProgress来在UI线程中执行onProgressUpdate
- 你可以从任何线程中取消任务的执行
Caution:你可能遇到的一个问题就是,当使用worker线程的时候,由于运行时的配置更改(屏幕方向旋转),worker线程意外的重启。要了解如何让你的任务persist,和当activity destroy的时候如何取消task,查看Shelves源码。
Thread-safe methods
有时候,你的methods可能被多个线程调用,应此需要保证线程安全。 对于可以被远程调用的方法,如bound service中的方法,这尤其重要。调用一个实现在IBinder中的方法时,如果调用者与IBinder在同一个进程,方法在调用者的线程中执行,但是,如果调用者和IBinder不在同一个进程中,方法在IBinder的进程中的某个线程中执行,这个线程是系统从线程池中选择的。由于Service可以有多个客户端,多个线程来同时执行IBinder中的方法,因此此时必须要保证线程安全。
Content provider同样也是从线程池中取线程来执行,因此也要保证线程安全。
Interprocess Comunication
android提供进程间交互,称作RPCs:一个方法被一个app component调用,但是在另外一个进程中执行,并返回结果给调用者。 要实现进程间通讯,app必须绑定一个service。详见Services手册。
--EOF--