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,我会单独的一篇文章讲解.


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

查看所有标签

猜你喜欢:

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

小白学运营

小白学运营

刘异、伍斌、赵强 / 电子工业出版社 / 2015-9-1 / 49.00元

《小白学运营》是针对网络游戏行业,产品运营及数据分析工作的入门读物,主要为了帮助刚入行或有意从事游戏产品运营和数据分析的朋友。 《小白学运营》没有烦琐的理论阐述,更接地气。基础运营部分可以理解为入门新人的to do list;用户营销部分则是对用户管理的概述,从用户需求及体验出发,说明产品运营与用户管理的依附关系;数据分析实战中,侧重业务分析,着重阐述的是分析框架,以虚拟案例的方式进行陈述,......一起来看看 《小白学运营》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具