探究Handler+Message机制的原理

我们经常会遇到这样一种情况,在子线程中去获取数据,然后需要在主线程里面使用数据,怎样将子线程中的数据发送到主线程中去呢?Handler+Message就具有这个能力,至于它是如何完成这个功能的就是我们今天要研究的主题。


首先来介绍一下它的几个核心成员:
1. Handler: 就是用来发送消息和处理消息的。
2. Message: 消息对象中封装了几个重要的信息,int what 作为一个标志与其他的Message进行区分 ,Object obj 这个就是我们要从子线程传到主线程的数据, long when MessageQueue就是根据这个属性来给Message排序的 Handler target 哪个Handler对象发送这个消息,那么后面也将由它来处理这个消息 。
3. Looper : 每个线程都只有一个Looper对象,主线程中的Looper对象是在ActivityThread中创建的,它的作用是不断地将MessageQueue里面的Message对象发送给Handler去处理。
4. MessageQueue : 相当是一个容器,用来存放Message对象的。

创建Handler对象必须要有Looper对象,如果没有就会报错,你可以去子线程试试new一个Handler,创建Looper对象的时候也会跟着创建一个MessageQueue,由于一个线程只有Looper对象,那么一个线程当然也就只有一个MessageQueue对象了。


我们还是从点到面的方式来分析:

   handler.sendMessage(Message message);
   handler.sendMessageDelayed(Message msg, long delayMillis);
   handler.sendMessageAtTime(Message msg, long uptimeMillis);
   handler.sendEmptyMessage(int what);
   handler.sendEmptyMessageDelayed(int what, long delayMillis);
   handler.sendEmptyMessageAtTime(int what, long uptimeMillis)

这是大家经常使用的发送消息的代码,其实这些发送消息的代码都走的是同一个方法

sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)

发送空消息虽然没有传入Message对象,但其实它里面走了Message msg = Message.obtain();这段代码也会给他一个Message对象,只是这个对象的obj属性是null的。

  public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

让我们看看这个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);
    }

由于一开始主线程就会创建一个Looper对象,创建Looper对象的同时就会创建一个MessageQueue,所以mQueue不可能为空,那么就会走enqueueMessage(queue, msg, uptimeMillis)这个方法,看名字就知道这个方法是将Message对象放入MessageQueque容器里面。

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

首先将这个发送Message的Handler对象赋值给这个Message对象的target属性,这样后面这个Message就可以通过这个target找到发送它的那个handler对象并交给他处理了。
最后走queue.enqueueMessage(msg, uptimeMillis)这个方法,这个一定是消息入队方法没跑了吧。

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

18 - 44行就是消息入队的过程,可以看到MessageQueue是通过双向链表的形式来管理Message的,里面有个无限for循环,将传进来的Message根据它身上的 long when来进行顺序的排列,这个when =SystemClock.uptimeMillis() + delayMillis,SystemClock.uptimeMillis()是开机到现在的时间,delayMilis如果你没有传入时间的话默认是为0的,那么就意味着这个消息马上会被处理。
handler.sendMessage 其实就是将消息发送到MessageQueue里面。

Looper对象的作用就是不停的将MessageQueue里面的Message对象发送给它对应的Handler去进行处理,在ActivityThread的Main方法里面调用了

public static final void main(String[] args) {
...
//创建Looper和MessageQueue
Looper.prepareMainLooper();
...
//轮询器开始轮询
Looper.loop();
...
}

Looper.prepareMainLooper()是用来创建Looper对象的,那么Looper.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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

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

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

Message msg = queue.next(); // might block
可以看到loop()方法里面也有一个无线的循环,它会一直去MessageQueue里面拿对象, 当然是根据Message的when属性来拿咯,如果现在的时间没有跟Message的when对应上,那么就拿不到对象,就会处于阻塞的状态,如果拿到了就调用msg.target.dispatchMessage(msg)方法,msg.target不就是找到之前发送这个消息的Handler对象吗?

  public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

由于Message的Callback没有被赋值,而mCallback一开始传进去的就是null,所以只会走handleMessage(msg)这个方法,这就是我们创建handler是重写的那个方法。

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

这个LooperThread其实就是ActivityThread主线程,所以这个run()方法是在主线程调用的,那么handleMessage(msg)当然也是在主线程中执行的咯。

还有一点点内容,我们经常也会看到这个方法:

handler.post(Runnable r)

 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

这个时候就将我们传进去的Runnable对象赋值给了Message的callback,也是调用sendMessageAtTime这个方法跟之前是一模一样的,还记得刚才

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

这个方法吗?这个时候的msg.callback 就不为null了啊,就会走handleCallback(msg)这个方法了啊:

  private static void handleCallback(Message message) {
        message.callback.run();
    }

message.callback不就是我们之前传进去的Runnable对象吗?所以就是调用我们传进去的Runnable的run()方法,要注意一点,这里并没有开启线程。

最后说一个项目中使用Handler容易造成handler内存泄漏的问题,由于

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

由于Handler相当于Activity的内部类,它是持有Activity的引用的,而且生命周期与Activity的生命周期不一样,这样当Activity被finish的时候,延迟任务的Message还存在于主线程中,它持有该Activity的Handler引用,所此时finish掉的Activity就不会被回收了,这就造成了内存泄漏。
解决方法是使用静态的Handler,在Handler的构造方法中传入Activity,并使用弱引用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值