android多线程通信机制:Thread,Handler,Looper,Message,MessageQueue

  android的多线程通信不仅在开发中非常重要,在我们找android开发相关的工作时,这个点也是很多技术考官会问到的,所以需要重点学习,以便理解其中的机制,能做到熟记于心,灵活运用。

  1.为什么要用多线程

   很简单,为了"并行"执行任务,常见的情况:1)下载文件,每个下载都用单独的一个线程来做,如果不用多线程那么下载任务就会排队,等待前一个文件下载完成之后才能开始进行其他下载。2)耗时操作需要在单独的线程中运行,不能在主线程中运行,以免界面假死。主线程是用来刷新界面、分发消息的,所以如果主线程中运行耗时操作,界面无法刷新、消息无法分发,必然造成界面没有响应。很多企业级软件都使用了多线程,例如word、vs.net

  2.关于android多线程构件简介

  1)Thread:在java中,线程被抽象为Thread类,我们需要从Thread类继承出一个子类来重写run方法,在run方法中实现自己的操作。

  2)Looper:每个线程都有唯一一个与之对应的Looper,Looper不断的从消息队列中取出消息来处理。

  3)Message:消息体,包含了需要在线程间传递的数据以及消息的处理者(handler)主要参数有what,arg1,arg2,obj,以及target。

  4)MessageQueue:消息队列,就是数据结构中说的队列,遵守先进先出规则。MessageQueue的构造函数的访问属性是package,所以我们无法实例化package,可见它是一个系统级的类。

  5)Handler:处理者,它的任务是在一个线程通过sendMessage(有多个重载)方法中发送消息,然后在另一个线程中通过handleMessage(有多个重载)方法处理消息,我们需要从该类继承出一个子类,并重写handleMessage方法来处理接受到的消息。

  3.通信机制

  首先我们需要明两个概念:

    1)主线程默认有一个Looper对象,我们自己创建的线程默认是没有Looper的,通常情况下,比如界面更新,我们都是子线程和主线程通信,而主线程有一个Looper,我们不用关心也看不到,所以我们会在代码中看不到Looper的身影,会以为子线程也有一个Looper,其实不是的,后面会讲到。

  2)从属关系不一定是通过引用来实现的,这个可能会引出更深层的问题。

  线程是消息产生的地方,在特定时刻会产生一个消息需要与其他线程共享,比如下载了整个文件的50%,主界面有一个进度条需要显示这个进度,那么子线程可以通过在主线程中创建的handler调用sendMessage方法把这个消息在子线程的run方法中发送出去,发送到主线程的消息队列,这时候主线程中的Looper就不断的从消息队列中取出消息来处理。来看一个简单的例子,这个实例很简单,界面是一个progressbar,我们在子线程中每个0.2秒就把进度发给主线程。

 1 public class MultiThreadActivity extends Activity {
 2     
 3     private ProgressBar progressBar = null;
 4     /** Called when the activity is first created. */
 5     @Override
 6     public void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.main);
 9         
10         this.progressBar = (ProgressBar)findViewById(R.id.progressBar1);
11         MyThread myThread = new MyThread(new MyHandler());
12         myThread.start();
13     }
14     
    //子线程 15 class MyThread extends Thread{
      //主线程中的handler实例
16 private Handler handler = null; 17 18 public MyThread(Handler h){ 19 this.handler = h; 20 } 21 @Override 22 public void run() { 23 // TODO Auto-generated method stub 24 super.run(); 25 for (int i = 0; i < 100; i++) { 26 try { 27 Thread.sleep(200); 28 Message msg = handler.obtainMessage(); 29 msg.arg1 = i;
             //发送消息到主线程
30 handler.sendMessage(msg); 31 } catch (InterruptedException e) { 32 // TODO Auto-generated catch block 33 e.printStackTrace(); 34 } 35 } 36 } 37 } 38 39 class MyHandler extends Handler{ 40 @Override 41 public void handleMessage(Message msg) { 42 // TODO Auto-generated method stub 43 super.handleMessage(msg);
         //处理消息的地方,这里是更新progressbar进度
44 progressBar.setProgress(msg.arg1); 45 } 46 } 47 }

我们在这段代码中只看到了Thread、Handler、Message类,难道没有用到MessageQueue和Looper吗,表面上看是没用到,但实际上谁都少不了,首先MessageQueue是系统来用的,对我们来说系统如何使用我们并不关心,至于Looper,因为这是子线程和主线程通信,所以Looper是内部支持的,看不到也没关系,下个子线程和子线程通信我们就可以看到如何使用它了。

看完这个简单的示例,我们以过程化的思维来看究竟发生了什么。

 

1.发送消息:

在OnCreate方法中,我们实例化一个MyThread类的实例,并调用start方法启动线程,线程的run方法中我用一个for循环模拟进度,注意以下代码

Message msg = handler.obtainMessage();Message的构造函数我们是能访问的,那为什么不new出来一个呢?android sdk建议我们使用Handler.obtainMessage方法,这是从一个消息池中取出消息,这样要比直接new出消息来得快,效率高,也节省内存,因为频繁的new出对象是很消耗性能的。
然后,将进度赋值给msg.arg1,我们可以在主线程中接受到这个msg并访问arg1。
 handler.sendMessage(msg);这个方法就把消息发送到主线程的消息队列中了,我们从源码中来验证一下:sendMessage实际调用的是sendMessageAtTime,源码如下

 1 public boolean sendMessageAtTime(Message msg, long uptimeMillis)
 2     {
 3         boolean sent = false;
 4         MessageQueue queue = mQueue;
 5         if (queue != null) {
 6             msg.target = this;
 7             sent = queue.enqueueMessage(msg, uptimeMillis);
 8         }
 9         else {
10             RuntimeException e = new RuntimeException(
11                 this + " sendMessageAtTime() called with no mQueue");
12             Log.w("Looper", e.getMessage(), e);
13         }
14         return sent;
15     }

其中mQueue是主线程的消息队列,这段代码:

msg.target = this;
很重要,msg保存了处理它的handler的实例,所以,即使多个子线程发送具有相同what参数的消息到主线程的消息队列,也不会搞混,消息会用自己保存的handler来处理自己。
queue.enqueueMessage(msg, uptimeMillis);
把msg放到queue中,等待looper把自己取出来处理。
2.取出消息:
消息是由looper从消息队列中取出的,源码如下:
 1 public static final void loop() {
 2         Looper me = myLooper();
 3         MessageQueue queue = me.mQueue;
 4         
 5         // Make sure the identity of this thread is that of the local process,
 6         // and keep track of what that identity token actually is.
 7         Binder.clearCallingIdentity();
 8         final long ident = Binder.clearCallingIdentity();
 9         
10         while (true) {
11             Message msg = queue.next(); // might block
12             //if (!me.mRun) {
13             //    break;
14             //}
15             if (msg != null) {
16                 if (msg.target == null) {
17                     // No target is a magic identifier for the quit message.
18                     return;
19                 }
20                 if (me.mLogging!= null) me.mLogging.println(
21                         ">>>>> Dispatching to " + msg.target + " "
22                         + msg.callback + ": " + msg.what
23                         );
24                 msg.target.dispatchMessage(msg);
25                 if (me.mLogging!= null) me.mLogging.println(
26                         "<<<<< Finished to    " + msg.target + " "
27                         + msg.callback);
28                 
29                 // Make sure that during the course of dispatching the
30                 // identity of the thread wasn't corrupted.
31                 final long newIdent = Binder.clearCallingIdentity();
32                 if (ident != newIdent) {
33                     Log.wtf("Looper", "Thread identity changed from 0x"
34                             + Long.toHexString(ident) + " to 0x"
35                             + Long.toHexString(newIdent) + " while dispatching to "
36                             + msg.target.getClass().getName() + " "
37                             + msg.callback + " what=" + msg.what);
38                 }
39                 
40                 msg.recycle();
41             }
42         }
43     }

这段代码首先取出当前线程的looper和messagequeue,然后用一个while(true)的死循环来不断调用message.next方法取出message,再调用msg.target.dispatchmessage方法处理消息

3.处理消息:
  来看一下Handler的dispatchMessage方法:
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

红色标记的代码,这个就是我们从handler继承出的子类中覆写的handleMessage方法。

至此整个过程已经很清楚了。我们在handleMessage可以发现一个有意思的地方,

msg.callback != null,然后
mCallback != null,再然后
handleMessage(msg);
可以看出我们实现的handleMessage方法的优先级别不是最高的。这也给我们更多的选择来实现消息处理。
 
子线程和子线程的通信
这个问题在我去凡客面试的时候被问到了,可见公司对了解android多线程通信机制要求还是比较高的。
之前我们说子线程默认是没有looper的,那么怎么办呢?必须是要创建出来一个looper了。
android sdk给出一个例子,如何为子线程创建消息循环:
class LooperThread extends Thread {
       public Handler mHandler;
       
       public void run() {
           Looper.prepare();
           
           mHandler = new Handler() {
               public void handleMessage(Message msg) {
                   // process incoming messages here
               }
           };
           
           Looper.loop();
       }
   }

这个结构必须是这样,主要是为了保证线程和Looper之间的一一对应,而且我们看到handler是在run方法中被实例化的,所以这个handler是属于这个线程的。我们来看一下Looper的prepare方法:

private static final ThreadLocal sThreadLocal = new ThreadLocal();
1
public static final void prepare() { 2 if (sThreadLocal.get() != null) { 3 throw new RuntimeException("Only one Looper may be created per thread"); 4 } 5 sThreadLocal.set(new Looper()); 6 }

Looper类有一个静态变量,类型是ThreadLocal,相当于一个map对象,它将当前线程作为key,new出来的Looper作为value保存了这种一一对应关系 

再来看一下Looper的构造函数:

private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

整个消息结构原来在这里被构造出来了,一个消息队列,当前线程、还有一个Looper,就等着其他线程给它发消息了。

好了,我们还是以一个示例来看一下如何使用:

  1 public class HelloworldActivity extends Activity {
  2     private static final int HANDLER1 = 1;
  3     private static final int HANDLER2 = 1;
  4     LooperThread lThread;
  5 
  6     /** Called when the activity is first created. */
  7     @Override
  8     public void onCreate(Bundle savedInstanceState) {,
  9         super.onCreate(savedInstanceState);
 10         setContentView(R.layout.main);
 11         
 12         lThread = new LooperThread();
 13         lThread.start();

 22         MyThread thread = new MyThread(new MyHandler("h1"),
 23                 new MyHandler1("h2"));
 24         thread.start();
 25         
 26         
 27     }
 28 
 29     public class MyThread extends Thread {
 30         private MyHandler handler1 = null; 32 
 33         public MyThread(MyHandler _handler1) {
 34             this.handler1 = _handler1; 36         }
 37 
 38         @Override
 39         public void run() {
 40             // TODO Auto-generated method stub
 41             super.run();
 42             for (int i = 0; i < 3; i++) {
 43                 try {
 44                     Thread.sleep(1000);
 45                     Message msg = this.handler1.obtainMessage();
 46                     msg.what = HANDLER1;
 47                     msg.obj = handler1.getName(); 53                     if(lThread.handler!=null){
 54                         Message msg3 = Message.obtain();
 55                         msg3.obj = "Hello this is from MyThread";
 56                         msg3.what = 101;
 57                         lThread.handler.sendMessage(msg3);
 58                     }
 59                     
 60                     
 61                 } catch (InterruptedException e) {
 62                     // TODO Auto-generated catch block
 63                     e.printStackTrace();
 64                 }
 65             }
 66         }
 67     }
 68 
 69     public class MyHandler extends Handler {
 70         private String name;
 71 
 72         public MyHandler(String _name) {
 73             this.name = _name;
 74         }
 75 
 76         @Override
 77         public void handleMessage(Message msg) {
 78             // TODO Auto-generated method stub
 79             super.handleMessage(msg);
 80             Log.i("tag", msg.obj + "");
 81         }
 82 
 83         public void setName(String name) {
 84             this.name = name;
 85         }
 86 
 87         public String getName() {
 88             return name;
 89         }
 90     }
 91 
 92     
115     public class LooperThread extends Thread {
116         public Handler handler;
117 
118         public void run() {
         //必须先调用这个方法
119 Looper.prepare(); 120 121 handler = new Handler() { 122 public void handleMessage(Message msg) { 123 // process incoming messages here 124 if(msg.what == 100 || msg.what == 101){ 125 if(msg.obj!=null){ 126 Log.i("LooperThread", msg.obj+""); 127 } 128 129 } 130 131 } 132 }; 133        //启动消息循环 134 Looper.loop(); 135 137 } 138 } 139 }

其中MyThread是一个普通的子线程,没有创建自己的消息循环,我们用它来发送消息给其他子线程。

LooperThread是一个带有消息循环的子线程,我们用它来接受MyThread传过来的消息。

输出结果:

LooperThread  Hello this is from MyThread

我们看到LooperThread接收到了MyThread传递过来的消息。

以上是我目前对android多线程通信的基本了解,第一次发博客,对android平台也是起步没有多久,希望看到这篇文章的朋友能多多指出不足,大家共同学习,共同进步。


 

 
 
posted @ 2012-07-05 10:24  小马奔跑  阅读(536)  评论(0)    收藏  举报