Android中利用Handler实现消息的分发机制(一)

上一篇文章,我们讲到在调用Handler的sendMessage方法时,最终我们会进入到一个叫 sendMessageAtTime的方法,如下:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

在这里,我们看到了MessageQueue和enqueueMessage等变量跟方法,我们可以想到,在Handler的实现的机制中,一定存在着一个消息队列,而它存放了我们创建的众多消息(Message)对象。

从这里,我们就会开始去探寻隐藏在 Handler对象后面的那一些我们想知道的实现机制了。

首先,我们还是从 Handler的创建开始说起,在上一篇文章,我们是通过 new Handler的方法来创建的,代码如下:

    private Handler mHandler = new Handler() {

        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_ID_1:
                Log.v("Test", "Toast called from Handler.sendMessage()");
                break;
            case MSG_ID_2:
                String str = (String) msg.obj;
                Log.v("Test", str);
                break;
            }
            
        }
    };

显然,我们要去看一下Handler的构造函数,如下:

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }
    
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

   
    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

我们可以看到,真正实现的构造函数,其实只有下面两个,如下:

    public Handler(Callback callback, boolean async) {
        ....
    }
    
    public Handler(Looper looper, Callback callback, boolean async) {
        ...
    }

这两个的差别就在于是否有参数Looper,而Looper是一个线程相关的对象。

何谓线程相关的变量?就是线程间不能共享的对象,只在本线程内有作用的对象。

那么Looper对象的作用是什么?

从我个人的理解,Looper类就是对MessageQueue的封装,它主要做的是两件事:

1)构造Looper对象,初始化MessageQueue,我们可以从其构造函数看到:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }


显然,MessageQueue正是在创建Looper的时候初始化的。

我们还注意到,这个构造函数是private的,而它则是被Looper.prepare方法调用的,如下:

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

可以看到,Loop对象被创建之后,会被放到ThreadLocal变量中,而ThreadLocal正是线程局部变量,这说明了关于Looper的一个特性:

每一个线程中都只能有一个Looper对象。

2)调用loop()方法,循环处理消息,具体代码如下:

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

       ....

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

           ...

            msg.target.dispatchMessage(msg);

           ....

            msg.recycle();
        }
    }

从上面的代码中,我们可以看到,在一个无限循环中,会从MessageQueue中获得消息,然后通过msg.target.dispatchMessage(msg)方法调用,处理消息,最后将消息进行回收。

在这里,我们先不关心dispatchMessage方法,我们先跑一下题,看一下recycle方法里面做了什么事吧,如下:

    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    
    public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

从上面的代码中,我们可以看到,Message对象本身有一个next的字段指向另外一个Message,也就是说,可以通过链表的方式将众多的Message给串起来,变成一个链表的消息池sPool。

而在这里,当调用recycle方法,就会将当前Message对象,先clearForRecycle之后,再添加到 sPool的头部中,而当我们通过Message的obtain方法的时候,我们其实也是从sPool中拿 出一个空的Message对象。

相信看到这里,大家就了解了上一篇文章中我说,为什么建议大家使用Message.obtain方法去获取消息对象了吧。

接下来,我们再回到正题上。

从上面关于Handler的创建和关于Looper的描述中,我们可以得出这样一个结论:

在每一个线程中,如果我们要创建Handler,那么此线程中就必须有一个Looper对象,而这个Looper对象中又封装了一个MessageQueue对象来对Message进行管理。

所以,如果我们要在一个新线程中使用handler的话,我们就必须通过调用Loop.prepare()方法,为此线程创建一个Looper对象,官方给出的代码如下:

  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();
      }
  }

当然,只有理论当然是不行的,我们还是得通过一个例子来看一下具体的效果,如下:

public class MainActivity extends ActionBarActivity {

    private static final int MSG_ID_1 = 1;
    private static final int MSG_ID_2 = 2;

    class LooperThread extends Thread {
        public Handler mHandler;

        public void run() {
            Looper.prepare();
    
            mHandler = new Handler() {                
                public void handleMessage(Message msg) {
                    Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId());
                    switch (msg.what) {
                    case MSG_ID_1:
                        Log.v("Test", "Toast called from Handler.sendMessage()");
                        break;
                    case MSG_ID_2:
                        String str = (String) msg.obj;
                        Log.v("Test", str);
                        break;
                    }
                }
            };
            Looper.loop();
        }
    }
    

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId());
        
        LooperThread looperThread = new LooperThread();
        looperThread.start();
        
        while(looperThread.mHandler == null){                        
        }
        
        Message message = Message.obtain();
        message.what = MSG_ID_1;
        looperThread.mHandler.sendMessage(message);

        Message msg2 = Message.obtain();
        msg2.obj = "I'm String from Message 2";
        msg2.what = MSG_ID_2;
        looperThread.mHandler.sendMessage(msg2);

    }
}

对应的结果如下:

10-27 16:48:44.519: V/Test(20837): Id of MainThread : 1
10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421
10-27 16:48:44.529: V/Test(20837): Toast called from Handler.sendMessage()
10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421
10-27 16:48:44.529: V/Test(20837): I'm String from Message 2

那其实在这里有一个问题,为什么我们平常可以直接去创建Handler对象,而不需要去调用UI线程的Looper.prepare和loop等方法呢?

当然,这肯定也是需要的,只是这一步是由Android系统帮我们做了,所以默认的主线程就不再需要去做这些初始化。

好了,这一篇文章,我们就了解了关于线程,Looper,Handler, MessageQueue 和 Message 等之间的一些关联,而主要是对于Looper对象的认识。

结束。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。