首先我们要明白的一个问题是Android为什么会设计这样一个“消息机制”,它有什么作用??

要解答这个问题,我们首先要了解,Andorid中的UI控件都是运行在我们应用程序中的主线程中,它们是非线程安全的,也就说所有涉及到UI的相关操作都只能在主进程中进行操作,如果要进行跨线程的操作会报异常。

错误示例代码如下:

                       
   1: TextView textView;
   2: Override
   3: ublic void onCreate(Bundle savedInstanceState) {
   4:    super.onCreate(savedInstanceState);
   5:    setContentView(R.layout.main);
   6:    
   7:    textView = (TextView) findViewById(R.id.textView);
   8:    Button btnAdd = (Button) findViewById(R.id.btnAdd);
   9:    btnAdd.setOnClickListener(new View.OnClickListener() {
  10:        
  11:        @Override
  12:        public void onClick(View v) {
  13:            
  14:            //开启一个线程执行更新UI操作
  15:            new Thread(new Runnable() {
  16:                
  17:                @Override
  18:                public void run() {
  19:                    
  20:                    //......执行一些费时的操作(省略)
  21:                    
  22:                    
  23:                    //执行成功之后将textview上的文字改为成功
  24:                    textView.setText("操作成功!");
  25:                    
  26:                }
  27:                
  28:            }).start();
  29:        }
  30:        
  31:    });



毫无疑问,上面的代码肯定会报错(textView是UI控件,只能在主线程进行操作)。那么要怎么解决这个问题呢,这时候就需要用到Android中的“消息机制”了,

要了解消息机制,我们先来了解下这里面涉及到五个关键对象:

Message: 消息对象,包含了一些描述信息和数据。
Handler: 负责消息对象的传递与处理(使用该类时我们必须先继承Handler类,然后重写Handler类中的handlerMessage(msg) 方法,在此方法中进行相关处理操作,如: 更新UI )
MessageQueue: 消息队列。存储由Handler发送过来的Message对象。
Looper: 负责为当前线程运行一个消息循环(MessageQueue)。它会循环抽取MessageQueue中的Message,并通过handler对象将其分发并处理。


消息机制的完整流程:

1. 生成消息

Message msg = handler.obtainMessage() / Message.obtain()

注意:虽然Message有公有的构造方法,但是建议还是使用上面写的这两种方法。因为,在调用这两个方法时,会去对象池中获取可重复利用的Message对象,避免了重新创建。

2. 发送消息

handler.sendMessage(msg); 或者 message.sendToTarget(); (它其实内部也是调用了handler.sendMessage(msg)方法)

接下来 我们通过分析源码来探究下“发送消息”这个操作的内部机制

通过查看源码我们可以了解sendMessage() 只是调用了Handler类中的sendMessageAtTime(),所以这里我们只用分析此方法就行-----源码如下:

类: Handler -451行

   1: public boolean sendMessageAtTime(Message msg, long uptimeMillis)
   2: {
   3:     boolean sent = false;
   4:     MessageQueue queue = mQueue; // 3. 问题:mQueue 从何而来????
   5:     if (queue != null) {
   6:         // 1. 将当前的handler对象保存在message的target属性中
   7:         msg.target = this;
   8:          // 2. 将message对象放入MessageQueue中
   9:         sent = queue.enqueueMessage(msg, uptimeMillis);
  10:     }
  11:     else {
  12:         RuntimeException e = new RuntimeException(
  13:             this + " sendMessageAtTime() called with no mQueue");
  14:         Log.w("Looper", e.getMessage(), e);
  15:     }
  16:     return sent;
  17: }

上面的这个方法,其实只是做了两个操作:

1. 将当前对象(handler)保存在message.target 中, 方便在处理消息对象时使用。

2. 将message对象存入到MessageQueue-- (入队)。 其实 MessageQueue 类似于我们的链表结构,我们入队的Message对象遵循“先进先出”的原则,越早被入队的Message,会被越早处理。

除了以上的这两个操作,上面的源码中还留了一个问题?

3. 该方法中出现的MessageQueue从何而来?? 下面我们来仔细探讨下:

从刚才的sendMessageAtTime() 我们知道, 将message入队到MessageQueue对象实际上是 Handler 类中一个成员变量 : mQueue 。它是在Handler的构造放被初始化的,接下来我们来看下源码中Handler的构造方法:

类: Handler -109行

   1: public Handler() {
   2:     if (FIND_POTENTIAL_LEAKS) {
   3:         final Class<? extends Handler> klass = getClass();
   4:         if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
   5:                 (klass.getModifiers() & Modifier.STATIC) == 0) {
   6:             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
   7:                 klass.getCanonicalName());
   8:         }
   9:     }
  10:     // 获取Looper对象
  11:     mLooper = Looper.myLooper();
  12:     if (mLooper == null) {
  13:         throw new RuntimeException(
  14:             "Can't create handler inside thread that has not called Looper.prepare()");
  15:     }
  16:     //从Looper对象中拿到MessageQueue对象并赋值给Handler类中的成员变量mQueue
  17:     mQueue = mLooper.mQueue;
  18:     mCallback = null;
  19: }

从以上代码可知,MessageQueue 对象 实际是位于Looper类中,,在构造handler对象的时候,我们从Looper中取出,并将其赋值给handle类中的成员变量。

到这里我想大家已经能明白: “发送消息”这个操作的内部操作:通过调用handler.sendMessage(msg) 方法,将传入的message对象传入到Looper对象(此对象唯一关联一个线程对象)的消息队列(MessageQueue)当中,等待被处理。

3. 执行消息

在第二步操作中我们已经知道,消息对象已经被handler发送到Looper中的MessageQueue中了。那么接下来该Message对象又做了什么呢??它最终又是怎么被执行的呢??

首先,我们先在Looper的源码中找到存放的消息对象的MessageQueue

   1: //.... 部分代码省略
   2: public class Looper {
   3:     
   4:     final MessageQueue mQueue;
   5:     
   6:     //在构造方法中对MessageQueue对象进行初始化
   7:     private Looper() {
   8:         
   9:         mQueue = new MessageQueue();
  10: 
  11:     }
  12: }

接下来我们来看下,传入的Looper类中MessageQueue中的消息对象又是怎么执行的呢?

类: Looper -106行

   1: public static final void loop() {
   2:     Looper me = myLooper();
   3:     MessageQueue queue = me.mQueue;
   4:     while (true) {
   5:         Message msg = queue.next(); 
   6:         if (msg != null) {
   7:             if (msg.target == null) {
   8:                 return;
   9:             }
  10:             msg.target.dispatchMessage(msg);
  11: 
  12:             msg.recycle();
  13:         }
  14:     }
  15: }

通过查看上面的源码我们看到,Looper类中有一个Loop() 方法,它里面维护了一个死循环,负责抽取该类中的MessageQueue中的消息对象,然后该消息对象被对应的handler对象(在第二步我们已经知道,handler对象已经被保存在msg的 target属性上了), 传到 handler 对象中的dispatchMessage方法中。 源码如下:

类: Handler -90行

   1: public void dispatchMessage(Message msg) {
   2:     if (msg.callback != null) {
   3:         handleCallback(msg);
   4:     } else {
   5:         if (mCallback != null) {
   6:             if (mCallback.handleMessage(msg)) {
   7:                 return;
   8:             }
   9:         }
  10:         handleMessage(msg);
  11:     }
  12: }

在此处方法中只是回调了handler.handlerMessage(msg) 方法, 该处理方法默认为空 。 所以,如果我们要对message进行的处理的话,必须继承Handler类然后重写handlerMessage方法,来完成自己的处理。例如:更新 UI。

好的,到这里Android的消息机制我们已经大概明白了, 过程如下:

①生成消息(生成Message对象)

–》

②发送消息(消息入队) –》

③ 执行消息

(Loop()方法将Message从消息队列中抽取出并传递到与Message相关的handler对象中)

– 》

我们重写handler方法中的handlerMessage() 进行相关处理

正确示例代码如下:

   1: public class HandlerDemoActivity extends Activity {
   2:    
   3:     TextView textView;
   4:     MyHandler myHandler;
   5:     
   6:     @Override
   7:     public void onCreate(Bundle savedInstanceState) {
   8:         super.onCreate(savedInstanceState);
   9:         setContentView(R.layout.main);
  10:         
  11:         //生成handler对象(注意: handler对象的创建位置非常关键,这个待会儿会详解)
  12:         myHandler = new MyHandler();
  13:         
  14:         textView = (TextView) findViewById(R.id.textView);
  15:         Button btnAdd = (Button) findViewById(R.id.btnAdd);
  16:         btnAdd.setOnClickListener(new View.OnClickListener() {
  17:             
  18:             @Override
  19:             public void onClick(View v) {
  20:                 
  21:                 //开启一个线程执行更新UI操作
  22:                 new Thread(new Runnable() {
  23:                     Handler handler;
  24:                     @Override
  25:                     public void run() {
  26:                         
  27:                         //......执行一些费时的操作(省略)
  28:                         
  29:                         
  30:                         //执行成功之后将textview上的文字改为成功
  31:                         //textView.setText("操作成功!");-- 这是错误的
  32:                         
  33:                         
  34:                         // 1. 生成消息
  35:                         Message message = handler.obtainMessage();
  36:                         //message.setData(data); 可以设置一些参数数据
  37:                         
  38:                         // 2. 发送消息
  39:                         handler.sendMessage(message);
  40:                         
  41:                         
  42:                     }
  43:                     
  44:                 }).start();
  45:             }
  46:             
  47:         });
  48:     }
  49:     
  50:     //继承Handler类,并重写handlerMessage方法
  51:     class MyHandler extends Handler{
  52:         
  53:         
  54:         public MyHandler() {
  55:             super();
  56:         
  57:         }
  58:         
  59:         //3. 处理Message
  60:         @Override
  61:         public void handleMessage(Message msg) {
  62:             
  63:             
  64:             textView.setText("操作成功!");
  65:             
  66:         }
  67:     }
  68: }

上面的代码是一个完整的Android消息机制实现,我们先来通过代码重温下 Android消息机制的过程:

① 示例代码第30行:

创建了一个消息对象(我们可以设置一些额外的数据,通过Message对象上的属性)

② 示例代码第39行:

将创建的消息对象发送出去,该消息对象会被发送到Looper中的MessageQueue中。

③Looper类源码第106行:( Loop() 方法 )

   1: public static final void loop() {
   2:     Looper me = myLooper();
   3:     MessageQueue queue = me.mQueue;
   4:     while (true) {
   5:         Message msg = queue.next(); 
   6:         if (msg != null) {
   7:             if (msg.target == null) {
   8:                 return;
   9:             }
  10:             msg.target.dispatchMessage(msg);
  11:  
  12:             msg.recycle();
  13:         }
  14:     }
  15: }

循环MessageQueue并调用所对应的hangdler对象的dispatchMessage(msg) 方法,将其分发出去,被分发出去的方法将会在Handler的handlerMessage方法接收到。

④ 示例代码第61行: (handlerMessage() 方法)

我们消息对象终于到到“目的地”,并且可以被处理了。

通过这个过程我们发现 Message对象是由handler发送,然后绕了一大圈儿之后又被送回到handler对象, 并进行相应的处理。 它之所以这么“麻烦”就是为了变相解决“在别的线程操作UI” 的问题。----- 在其它线程中进行一些相关操作(一般是比较费时的)后,并不是在当前线程中更新UI,而是传递一个消息到与UI线程相关联的Looper中, 然后会在Looper中的Loop方法被抽取并传送至handler对象的handlerMessage方法。

最后,我们还要解决一个疑问就是: 为什么能在handler中进行UI操作 -- textView.setText("操作成功!"); --- 示例代码 39 行

答案的关键点就是handler对象的创建位置:

   1: public class HandlerDemoActivity extends Activity {
   2:    
   3:     TextView textView;
   4:     MyHandler myHandler;
   5:     
   6:     @Override
   7:     public void onCreate(Bundle savedInstanceState) {
   8:         super.onCreate(savedInstanceState);
   9:         setContentView(R.layout.main);
  10:         
  11:         //生成handler对象(此操作是在UI主线程中)
  12:         myHandler = new MyHandler();
  13: 
  14:        //..........以下代码省略

从上面代码我们看出来我们的handler对象实际是在UI线程中创建的,,当我们创建在UI线程中创建handler对象时,android会将此对象和当前UI线程对象中的消息队列绑定, 源码如下:

   1: public Handler() {
   2: 
   3:     //.... 部分代码省略    
   4:     
   5:     //从当前线程中取出Looper对象并向其赋值给Handler的成员变量mLooper
   6:     mLooper = Looper.myLooper();
   7:     if (mLooper == null) {
   8:         throw new RuntimeException(
   9:             "Can't create handler inside thread that has not called Looper.prepare()");
  10:     }
  11:     // 从Looper中取出MessageQueue对象并赋值
  12:     mQueue = mLooper.mQueue;
  13:     mCallback = null;
  14: }

下面是Looper.myLooper()方法的源码:

   1: public static final Looper myLooper() {
   2:     //从当前线程中获取Looper对象
   3:     return (Looper)sThreadLocal.get();
   4: }

通过这两段代码我们就明白了,handler其实是间接关联(handler关联Looper,而Looper又唯一对应一个线程,并负责为当前线程运行消息循环)当前线程对象,如果当前线程是UI线程的话那么我们就可以处理同在UI线程中的UI控件了。

另外,有一点要提醒的就是,如果我们的Handler对象不是在UI线程中进行初始化的,那么我们就得自己为当前线程构造一个Looper对象,如果要运行的话还得自己启动位于Looper中的消息循环(Loop()方法). 示例代码如下:

   1: class LooperThread extends Thread {
   2:      public Handler mHandler;
   3: 
   4:      public void run() {
   5:          //构造一个Looper对象,并向其放入到当前线程中(感兴趣的同学可以通过源码查看明细)
   6:          Looper.prepare();
   7: 
   8:          mHandler = new Handler() {
   9:              public void handleMessage(Message msg) {
  10:                  // process incoming messages here
  11:              }
  12:          };
  13:          
  14:          //启动消息处理循环
  15:          Looper.loop();
  16:      }
  17:  }

如果handler是在UI线程中进行构造的,那么上面代码中注释的两句代码,android会自动帮我们执行。

ok, 到此,此文结束!

更多相关文章

  1. Activity的启动流程分析
  2. Android(安卓)数据Parcel序列化过程源码分析
  3. 《Android开发艺术探索》读书笔记 (2) 第2章 IPC机制
  4. 基于xmpp openfire smack开发之Android消息推送技术原理分析和实
  5. Android开发中的Handler线程初窥,及HandlerThread异步通信的实现
  6. Android开发秘籍学习笔记(三)
  7. android发送模拟按键消息,出现死锁,timeout的解决方法
  8. android 的消息处理
  9. 转自 老罗 Android应用程序资源管理器(Asset Manager)的创建过程分

随机推荐

  1. Android之drawable state各个属性详解
  2. 《Android面试宝典》学习笔记(第二章:布局)
  3. android ellipsize的使用
  4. Android(安卓)JNI简单实例(android 调用C/
  5. [转]Android(安卓)图形系统剖析
  6. android ellipsize的使用
  7. 使用NetBeans+Android(安卓)SDK+NBAndroi
  8. android:paddingLeft 和 layout_marginLe
  9. android常用组件之TextView组件
  10. Android(安卓)4.2启动代码分析(一)