Android在android.os包中定义了两个类,它们通常是多线程应用线程间通信的基石:Handler和Looper
AsyncTask对象隐藏了Handler和Looper的细节,在某些情况还是要直接跟Handler和Looper打交道,比如把Runnnable对象传递到主线程之外的线程
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
Handler的使用:
public class MyThread extends Thread{
private static final String TAG="MyThread";
private Handler mHandler;
public MyThread(String name) {
super(name);
}
public Handler getmHandler() {
return mHandler;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
Looper.prepare();//把Looper绑定到此线程
mHandler=new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
//处理消息
}
}
};
//Handler绑定到此线程的Looper
Looper.loop();//调用loop()去启动消息循环
}
}
注:Handler对象在run()方法中创建,因为它需要绑定到指定的Looper,这个Looper就是在run()方法中调用Looper.prepare()创建的,因此在线程产生之前,调用getHandler() 将返回null
Looper的使用:
public class MyHandlerThread extends HandlerThread{
private static final String TAG="MyHandlerThread";
private Handler mHandler;
public MyHandlerThread(String name) {
super(name);
}
public Handler getmHandler() {
return mHandler;
}
public void start() {
// TODO Auto-generated method stub
super.start();
Looper looper=getLooper();//这里会一直阻塞到线程的Looper对象初始化结束
mHandler=new Handler(looper){
public void handleMessage(Message msg){
switch(msg.what){
//处理消息
}
}
};
}
}
android中的线程
1. 不要阻塞UI线程. 如果在UI线程中执行阻塞或者耗时操作会导致UI线程无法响应用户请求.
2. 不能在非UI线程(也称为工作线程)中更新UI, 这是因为android的UI控件都是线程不安全的.
由上所述, 开发者经常会启动工作线程完成耗时操作或阻塞操作, 如果需要在工作线程的执行期间更新UI状态, 则应该通知UI线程来进行.
线程间通信
请看下面的代码:
- public void onClick(View v) {
- new Thread(new Runnable() {
- public void run() {
- Bitmap b = loadImageFromNetwork("http://example.com/image.png");
- mImageView.setImageBitmap(b);
- }
- }).start();
- }
上面的代码是错误的, mImageView.setImageBitmap(b)违反了第二条准则--不能在工作线程中更新UI.
线程间通信可以解决工作线程如何通知UI线程更新控件的问题. android提供了3种线程间通信的方案:
1. 调用以下方法:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
如果在工作线程中调用了这3个方法, 那么方法中Runnable参数封装的操作会在UI线程中执行.
使用这种方式可以修正例子中的错误之处:
- 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() {
- // run方法会在UI线程中执行
- public void run() {
- mImageView.setImageBitmap(bitmap);
- }
- });
- }
- }).start();
- }
2. Handler机制. Handler机制允许开发者在工作线程中调用与UI线程绑定的handler对象的sendMessage()方法向UI线程的消息队列发送一条消息, UI线程会在适当的时候从消息队列中取出消息并完成处理.
3. 使用AsyncTask类. 创建一个AsyncTask类的子类, 并根据需要选择覆写onPreExecute(), doInBackground(), onProgressUpdate(), onPostExecute()方法.AsyncTask类的具体使用方法请参看文档, 以下是一些大概的说明:
a. AsyncTask类是一个泛型类, 存在3个泛型参数. 第一个参数指定execute方法的参数类型, 第二个参数指定onProgressUpdate()方法的参数类型, 第三个参数指定 doInBackground()方法的返回值类型以及onPostExecute()方法的参数类型.
b. 执行流程: 在UI线程中调用AsyncTask类的execute方法(只有该步骤是由程序员控制的)-->系统调用onPreExecute(), 这个方法在UI线程中执行-->系统调用doInBackground()方法, 这个方法在工作线程中执行-->在doInBackground()方法中每调用一次publishProgress()方法, 就会在UI线程中执行一次onProgressUpdate()方法-->doInBackground()方法执行完成后, 系统将调用 onPostExecute()方法, 并将doInBackground()方法的返回值传递给 onPostExecute()方法的形参. onPostExecute()方法在UI线程中执行.
采用这种方式也可以修正例子中的错误之处:
- public void onClick(View v) {
- new DownloadImageTask().execute("http://example.com/image.png");
- }
- private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
- protected Bitmap doInBackground(String... urls) {
- return loadImageFromNetwork(urls[0]);
- }
- protected void onPostExecute(Bitmap result) {
- mImageView.setImageBitmap(result);
- }
- }