Android消息机制之Handler原理解析

在平时开发中经常会使用Handler发送消息,那么Handler到底是什么呢?

Handler是Android系统提供的一套消息处理机制,通过它可以实现线程间数据传递。

在开发中会将涉及到耗时操作放入子线程中执行,待执行完毕后需要将直接结果更新至主线程,这时就需要用到Handler。当然Handler不仅仅只能将子线程的数据传递至主线程,它可以实现任意两个线程间的通信哦!看完本篇文章,相信你就会了解其内部原理了。

讲解Handler机制,我们就从基本用法开始吧!

public class HandlerActivity extends Activity {

    private static final int MSG_RESULT = 0;

    private static Handler mHandler;
    private static TextView mText_tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mText_tv = findViewById(R.id.sample_text);
        setMsg();
    }

    private void setMsg() {
        if (mHandler == null) {
            //创建Handler对象
            mHandler = new MyHandler();
        }
        //模拟子线程耗时操作,并将结果发送至主线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);//模拟耗时操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //创建Message消息对象
                Message msg = Message.obtain();
                msg.what = MyHandler.MSG_RESULT;
                msg.obj = "success";
                //发送消息
                mHandler.sendMessage(msg);
            }
        }).start();
    }

    //创建Hanlder子类 实现其handleMessage方法 
    private static class MyHandler extends Handler {
        private static final int MSG_RESULT = 0;

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RESULT:
                    String result = (String) msg.obj;
                    mText_tv.setText(result);
                    break;
            }
        }
    }
}

这样一个简单的Handler使用事例就完成了,可以看到在子线程中调用Handler中sendMessage(msg),就完成了消息发送,接着在主线程中就会回调handleMessage(Message msg),很神奇!这是我第一次使用Handler时给我的感觉,那么我们进入正题吧!

既然是通过Handler发送消息,那么我们就来看Handler中sendMessage(msg)到底是怎么发送消息。

Handler#sendMessage(Message msg) 发送消息

//发送消息
public final boolean sendMessage(Message msg)
    {    
        //调用了发送延时消息   延时为0
        return sendMessageDelayed(msg, 0);
    }

//发送一个延时消息
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

//及时发送消息
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);
    }

/**
这里有必要解释下,Handler除了可以通过sendMessage发送消息外,还可以通过sendEmptyMessage、sendMessageDelayed、sendMessageAtTime、
sendEmptyMessageDelayed等发送消息,根据需求的不同可以选择不同的方式发送消息,但是这些发送消息方法最后都会走进sendMessageAtTime。
**/

这里 我想先介绍一个类Message ,发送的消息通过该类的封装,在线程间传递,这里就来解开它神秘的面纱。

Message  消息对象

/**
 * While the constructor of Message is public, the best way to get
 * one of these is to call {@link #obtain Message.obtain()} or one of the
 * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
 * them from a pool of recycled objects.
 */  
//以上注释是对Message 的解释,大意是:虽然Message 的构造函数是公共的(也就是可以通过new Message()创建该对象),但是推荐使用Message.obtain()获取该对象,其内部维护了一个回收池。
public final class Message implements Parcelable{
    
    ***//其他属性以及函数由于跟本次分析关系不大,故没有完全列出

    public int what;//当前消息标识
    
    public Object obj;//当前消息传递数据对象

    long when;//当前消息延时时间
    
    Handler target;//当前消息对应的Handler
    
    Runnable callback;//当前消息回调对象
      
    Message next;//下一个消息对象

    public static final Object sPoolSync = new Object();//加锁对象
    
    private static Message sPool;//sPool 指向链表的头部
    
    private static int sPoolSize = 0;//初始大小
    
    private static final int MAX_POOL_SIZE = 50;//最大值
    
    //获取Message,如果单项链表中不为空,则从中获取Message,否则创建Message
    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;//如果单向链表不为空,获取链表头的Message ,并将链表头后移,简单说就是从单向链表中断开链表头,从中拿取Message 对象,当前回收池大小减1
            }
        }
        //初次获取sPool 必定为空,故创建Message
        return new Message();
    }
    
    //回收Message,将所有属性重置,并将当前Message对象返还至单项链表中
   void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {//当前回收池大小小于最大值
                next = sPool;
                sPool = this;
                sPoolSize++;
                //上面三行代码将当前Message的next指向sPool,也就是将当前Message当成新的链表头部,回收池大小加1
            }
        }
    }
    
}

说了一大堆,估计各位还是一脸懵逼,这里再给出张图解,配合上述解释,效果更佳。

我们回到Handler发送消息,所有发送消息的方法最后都会调用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;
        }
        //调用enqueueMessage
        return enqueueMessage(queue, msg, uptimeMillis);
    }


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //target 赋值为当前Handler
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //调用MessageQueue的enqueueMessage
        return queue.enqueueMessage(msg, uptimeMillis);
    }

发现上述代码,将传入的消息与当前发送对象Handler绑定,并将其传入至MeassageQueue中,进入MeassageQueue中一探究竟。

MeassageQueue#enqueueMessage 消息进入消息队列

boolean enqueueMessage(Message msg, long when) {
        //如果msg没有绑定Handler则会抛出异常
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //如果当前msg在被使用,则抛出异常
        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被使用
            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;
    }

上述代码主要将消息放入消息队列。

那么放进消息队列的消息,怎样取出来的呢?Handler发送消息至MeassageQueue中,那么消息的获取肯定也在MeassageQueue中。bingo!消息的获取就在MeassageQueue中的next()中。

MeassageQueue#next 从消息队列中获取消息

Message next() {

        ***

        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //该方法通过调用native方法,阻塞当前调用线程nextPollTimeoutMillis毫秒
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {//如果当前时间还未满足队列头部Message触发时间,则获取剩余时间,
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {//获取消息并返回
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

              ***

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
 }

在上述next()中,主要是从消息队列中获取消息,其中会调用nativePollOnce(ptr, nextPollTimeoutMillis),该方法会阻塞当前调用栈线程nextPollTimeoutMillis毫秒,其中nextPollTimeoutMillis取值不同,其阻塞情况也不同。

  • nextPollTimeoutMillis小于0,一直被阻塞,直到被唤醒
  • nextPollTimeoutMillis等于0,不阻塞
  • nextPollTimeoutMillis大于0,最长阻塞nextPollTimeoutMillis毫秒,再次期间如被唤醒,则会立即返回

通过上述代码可知,nextPollTimeoutMillis初始值为0,如果获取到Message则直接返回,否则会一直阻塞当前线程,知道其他地方调用native方法nativeWake(long)将其唤醒。在消息进消息队列时调用过nativeWake(long)。

获取消息的方法找到了,那么是在哪里调用的呢?这里就要讲到大家都听说过的Looper了。

Looper

Looper在消息机制中扮演者举足轻重的的角色,通过它不停的从消息队列中拿取消息,如果获取到消息就将该消息返回,否则就任由消息队列阻塞,直到有新消息进入队列。

/**<p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  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();
  *      }
  *  }</pre>
  */
//通过上述注释可以大概明白Looper的作用以及其用法。
//通过使用Looper中prepare以及loop来创建一个初始Hanlder,并且给出了使用案列
public final class Looper {
    
    ***
       
    
     private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    
    public static void prepare() {
        prepare(true);
    }
    
     //Looper对象与当前线程绑定,sThreadLocal是ThreadLocal<Looper>的对象,ThreadLocal中存储
的值更当前所处线程相关,具体原理请自行百度,这里就不多做介绍,只要明白ThreadLocal存储值与线程相关
    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));
    }

    ///该方法在主线程中调用   ActivityThread中Main方法中
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
        
    
    //一直从消息队列中拿消息
   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;
            }

            ***

            
            try {
                //调用msg对应Handler中dispatchMessage方法
                msg.target.dispatchMessage(msg);
                ***
            } finally {
               ***
            }
            //回收当前Message
            msg.recycleUnchecked();
        }
    }
    
    //获取looper
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    
***


}

上面我列出了Looper中需要用到的方法,这些方法是在哪里调用的呢?接下来一一为各位揭晓。

通过调用Looper.loop()可不停从消息队列中获取消息,那么哪里调用了该方法呢?文章开始我们写的事例中并没有调用Looper.loop()啊!但是为什么还是可以获取到消息呢?这是因为系统已经帮我做了,在主线程中其实调用了Looper.looper()。在主线程创建时在其Main()方法中调用了该方法,不信我们就一起去看看。

ActivityThread#Main

public static void main(String[] args) {
***
Looper.prepareMainLooper();

***

if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

***

Looper.loop();


***
}

在调用loop方法前先调用了prepareMainLooper,进去看看。

Looper#prepareMainLooper

 public static void prepareMainLooper() {
        //调用prepare
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //调用myLooper 获取Looper对象
            sMainLooper = myLooper();
        }
    }

  //创建Looper对象,并将其存储在线程相关ThreadLocal中
  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));
    }

这里说白了就是创建一个Looper对象,将其放入ThreadLocal中,其用意就是在每个线程中,looper只能有一个实例。

这里再看Looper.loop。

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;

        ***

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {//如果当前队列没有消息,则让其一直处于阻塞状态
                // No message indicates that the message queue is quitting.
                return;
            }

            ***

            
            try {
                //调用msg对应Handler中dispatchMessage方法
                msg.target.dispatchMessage(msg);
                ***
            } finally {
               ***
            }
            //回收当前Message
            msg.recycleUnchecked();
        }
    }

    
    //获取当前线程对应looper
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

当获取到消息时会调用msg.target.dispatchMessage(msg),也就是该消息对应Handler对象的dispatchMessage,这里msg.target的赋值是在消息进入MessageQueue(消息队列)之前,不知到家是否还记得呢?我们接着看。

Handler#dispatchMessage 分发消息

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//如果调用Handler中post***开头方法,会传入一个Runnable对象,此时就会调用其run方法
            handleCallback(msg);
        } else {
            if (mCallback != null) {//如果在Handler构造方法中传入Callback对象,则会调用其handleMessage方法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//否则会调用handleMessage方法。该方法是个空实现,需要重写该方法才能处理后续逻辑
        }
    }

至此,整个Handler消息机制分析结束了,这里总结下整个消息机制。

总结

使用Handler时,首先会在当前线程维护唯一一个Looper,每个Lopper对应一个消息队列MessageQueue,也就是说一个线程只能有一个Looper、一个MessageQueue,但是可以用多个Handler对象。Looper.loop()会不停的从消息队列中获取消息,如果队列中没有消息或者当前消息触发条件不满足,则会阻塞当前调用栈线程;如果获取到消息就会调用其对应Handler所在线程。

最后给张图:

相信大家对Handler消息机制有了全面的了解。由于佩琪水平有限,文中难免出现错误,欢迎大家给出意见!

 

 

参考

南尘:https://mp.weixin.qq.com/s/ReSmYXFr_gH_39g9LevqlA

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值