Android 消息机制 - Handler 实现原理深入剖析

栏目: 后端 · 发布时间: 5年前

内容简介:Android 的消息机制Handler,是在开发过程中都会用到的,它可以轻松的从子线程切换到主线程,大部分的时候我们将Handler会用在更新UI的操作.那么Handler是如何做到不同线程通信的呢? 如何切换线程的呢?本篇文章带领大家来轻松的实现一个Handler,并深入的去了解Handler的实现原理在讲解之前,我们先来了解一下Handler的工作过程,至于如何使用Handler,这里我就不在复述了,只要做过Android开发我相信大家都会使用.Handler发送消息是通过Looper以及Messag

Android 的消息机制Handler,是在开发过程中都会用到的,它可以轻松的从子线程切换到主线程,大部分的时候我们将Handler会用在更新UI的操作.那么Handler是如何做到不同线程通信的呢? 如何切换线程的呢?本篇文章带领大家来轻松的实现一个Handler,并深入的去了解Handler的实现原理

在讲解之前,我们先来了解一下Handler的工作过程,至于如何使用Handler,这里我就不在复述了,只要做过Android开发我相信大家都会使用.

Handler的工作过程

Handler发送消息是通过Looper以及MessageQueue协同工作的.

Looper的初始化:在应用启动时ActivityThread会创建一个 Looper.prepare() ,并调用 Looper.looper() 方法无限循环等待是否有新的消息.

发送消息:通过Handler的send方法发送消息,会调用 MessageQueue.enqueue() 方法,此时消息(Message)会被加入MessageQueue消息队列中,已知在Looper初始化是, Looper.looper() 一直在监听是否存在新的消息,此时Looper发现有新消息到来,就会处理该消息,最终会调用 Handler.handleMessage() 方法,Looper是运行在创建Handler的线程中的, handleMessage 一定在创建Handler的线程中去执行.

这个过程用图来表示

Android 消息机制 - Handler 实现原理深入剖析

通过上述简单的解了Handler的工作过程,接下来我们就根据上述过程来实现一个简单的Handler,然后我们在带着问题去看Handler的源码.

手写一个Handler

  • 首先创建四个类:Handler、Message、MessageQueue、Looper

我们先实现核心的Looper类:

通过一个静态的ThreadLocal来存储Looper对象, prepare() 方法来初始化looper

//存储Looper
    private static final ThreadLocal<Looper> mThreadLooper = new ThreadLocal<>();

    //looper中存在一个消息队列
    public MessageQueue messageQueue;

    public Looper() {
        messageQueue = new MessageQueue();
    }

    /**
     * 准备 准备时就是在主线程中准备的
     */
    public static void prepare() {
        //ThreadLocal 会拿到当前的线程
        if (null != mThreadLooper.get()) {
            throw new RuntimeException(Thread.currentThread() + ":已经有了looper");
        }
        mThreadLooper.set(new Looper());
    }

然后再写一个获取Looper的静态方法 myLooper() 主要是给Handler调用

/**
     * 获得当前线程的looper
     *
     * @return
     */
    public static Looper myLooper() {
        return mThreadLooper.get();
    }

Looper还有一个核心的方法,就是实现轮询器去查询新的消息,我们知道要通过消息队列MessageQueue来查询是否有新的消息,MessageQueue存储的为Message,我们先来实现Message,Message类为链式结构

public int what;

    public Object obj;

    //下一个消息
    public Message next;

    //使用此handler发送的消息,则需要分发到这个handler处理
    public Handler target;

    public void recyle() {
        obj = null;
        next = null;
        target = null;
    }

MessageQueue来存储Message,MessageQueue需要实现两个方法,一个是加入队列 enqueue ,一个是获取尾部的队列 next ,Looper轮询去就是调用 next 方法来获取最新的消息,当next没有最新消息时,处于阻塞状态 wait() ,当有入队 enqueue 操作时,需要通知 next 不用处于阻塞状态了 notify() ,我有新消息了,注意入队和取队需要同步synchronized.

入队操作

/**
 * 将消息塞入队列
 *
 * @param message
 */
public void enqueue(Message message) {
    //有入队就有取队 需要同步一下
    synchronized (this) {
        Message m = mMessage;
        if (null == m) {
            mMessage = message;
        } else {
            //循环判断是否在链表尾端
            Message per;
            do {
                per = m;
                m = per.next;
            } while (null != m);
            per.next = message;
        }
        //通知获取 message解除阻塞
        notify();
    }
}

取队操作

/**
 * 获取消息
 *
 * @return Message
 */
Message next() {
    synchronized (this) {
        Message message;
        for (; ; ) {
            message = this.mMessage;
            if (null != message) {
                break;
            } else {
                //等待 阻塞状态
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        mMessage = mMessage.next;
        return message;
    }
}

MessageQueue我们已经实现了,接下来可以实现Looper的轮询器了,来监听新消息

/**
     * 不停的从MessageQueue中取出Message
     */
    public static void looper() {
        Looper looper = Looper.myLooper();
        MessageQueue messageQueue = looper.messageQueue;
        for (; ; ) {
            //不停的获取message
            Message message = messageQueue.next();
            if (message == null) {
                break;
            }
            //分发到发送message的handler执行
            message.target.handleMessage(message);
        }
    }

接下来,实现Handler方法,我们在Handler中获取Looper,并通过Looper获取MessageQueue发送消息时加入消息队列.

public class Handler {
    private Looper looper;

    private MessageQueue messageQueue;

    public Handler() {
        looper = Looper.myLooper();
        //拿到当前线程的消息队列
        messageQueue = looper.messageQueue;
    }

    public void sendMessage(Message message) {
        message.target = this;
        //将此消息加入消息队列
        messageQueue.enqueue(message);
    }

    public void handleMessage(Message message) {

    }
}

整体的消息机制代码,我们已经实现了,接下来模拟一下handler的发送过程,第一步初始化Looper,然后开启Looper轮询器监听消息,Handler发送接收消息

public void testHandler() {
      Looper.prepare();//looper 处于准备状态
      
      final Handler handler = new Handler() {
          @Override
          public void handleMessage(Message message) {
              System.out.println(Thread.currentThread() + " ");
          }
      };

      new Thread(new Runnable() {
          @Override
          public void run() {
              handler.sendMessage(new Message());
          }
      }).start();

      Looper.looper();
  }

OK,通过上述手写实现简单的handler,相信读者一定明白了,Handler的核心思想.

Handler源码分析

彻底明白Handler的工作过程后,我们就能很彻底的看懂Handler源码的实现了

还是按照上面,手写Handler的实现来分析Android Handler源码.

Looper源码分析

首先看到,熟悉的成员属性

// sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;

再来看 prepare() 方法的实现,将Looper存储到ThreadLocal中去

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

同样也实现了 myLooper 方法,来提供其他类调用Looper

/**
    * Return the Looper object associated with the current thread.  Returns
    * null if the calling thread is not associated with a Looper.
    */
   public static @Nullable Looper myLooper() {
       return sThreadLocal.get();
   }

Android是如何初始化Looper呢?来看ActivityThread源码的main方法,代码如下,对Looper初始化,并开启了轮询器

public static void main(String[] args) {
      

      .......

       Looper.prepareMainLooper();

      
       .....
       Looper.loop();
   }

prepareMainLooper方法很简单,实际上调用了prepare和myLooper方法

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

轮询器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;

      ........
       //无限循环获取Message
       for (;;) {
           Message msg = queue.next(); // might block 没有新的消息就不执行
           if (msg == null) {
               // No message indicates that the message queue is quitting.
               return;
           }
            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
           final long dispatchEnd;
           try {
//如果存在新消息获取target 就是当前的Handler 然后调用dispatchMessage处理消息
msg.target.dispatchMessage(msg);
               dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
           } finally {
               if (traceTag != 0) {
                   Trace.traceEnd(traceTag);
               }
           }
           .....
           }
           
           ......
           }

再来看Handler的dispatchMessage,实际上就是处理消息

public void dispatchMessage(Message msg) {       
        //如果message的callback实际上是一个Runnable,就调用run方法
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //如果Handler设置了回调就调用回调
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //直接调用handleMessage
            handleMessage(msg);
        }
    }

Handler源码分析

主要看handler发送消息的代码,Handler在初始化时,构造函数中,获取了Looper和MessageQueue,

public Handler(Callback callback, boolean async) {
        ......

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler中发送消息有好几种,我们只看最简单的一个

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    
    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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

上述代码实现还是非常简单的,实际上就是将消息添加到消息队列中.

MessageQueue源码

再来看一下MessageQueue的入队和出队

入队操作代码入下,实际上就是添加到Message单链表的尾部

boolean enqueueMessage(Message msg, long when) {
        //判断msg当前的handler是否为空
        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) {
                // 这一段为核心代码
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //
                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;
            }

            // 这段代码就是通知 next不用处于阻塞状态了,有新的消息过来了,注意这里并没有用notify()方法,而是使用底层C++的一个方法,想要了解的可以看底层的源码
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

再来看一下next方法,此方法也是Looper.loop()轮询器轮询判断是否有新消息的核心方法

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) {
                        // 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;
                }
                }

OK,以上就是整个Handler源码工作的核心代码,至于ThreadLocal,我会单独的一篇文章讲解.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

优秀网站设计

优秀网站设计

林奇 (Patrick J.Lynch)、霍顿 (Sarah Horton) / 机械工业出版社 / 2012-10-1 / 69.00元

优秀网站设计:打造有吸引力的网站(原书第3版),ISBN:9787111399599,作者:(美)Patrick J. Lynch Sarah Horton 著,李静等译一起来看看 《优秀网站设计》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具