Handle 源码分析以及手写实现

版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、源码分析

1.消息入队

我们比较常用的 Handle 的方法是 sendMessage 这个方法,查看其源码。

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

sendMessage 直接调用了 sendMessageDelayed 这个方法,继续查看 sendMessageDelayed 方法源码。

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

sendMessageDelayed 调用了 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);
    }

sendMessageAtTime 里面调用了 enqueueMessage 方法。

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

enqueueMessage 中调用了成员变量的 MessageQueue 的 enqueueMessage 方法,在这里实现了消息入队

2.消息出队

在 Handle 的成员变量里有一个 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;

        // 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

            ...

            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            ...
        }
    }

在里面使用了一个死循环进行轮询,不停的调用 queue 的 next 方法获取下一个,根据注解这个方法可能被阻塞,然后调用了 msg.target.dispatchMessage(msg), msg.target 就是我们创建的那个 Handle,即又调用了 Handle 的 dispatchMessage 方法。

Handle 的 dispatchMessage :

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

创建 Handle 的时候是可以传递一个回调,如果这个回调存在的话则调用该回调方法,正常情况下我们是没有进行设置回调,所以调用到了 handleMessage 这个方法。

Handle 的 handleMessage:

    public void handleMessage(Message msg) {
    }

Handle 的 handleMessage 是一个空实现,所以我们使用 Handle 的时候,需要对这个方法进行重写。

3.大体流程

这里写图片描述

Handler 发送消息,保存到 MessageQueue,通过轮询器 Looper 不停的进行轮询,获取 MessageQueue 中的消息,传递给 Handler,调用 Handler 的 handleMessage 方法。

这是一个消息的机制,并不是说 Handler 这个消息机制是用来更新 UI,主要的作用是在主线程与子线程之间进行通信,经典例子就是在子线程中调用 UI 主线程,进而更新 UI 界面。

4.Handler 与 Looper、MessageQueue

每一个 Handler 下有一个 Looper mLooper 和 MessageQueue mQueue,这个是在 Handler 的构造函数中获取到的,即一创建 Handler 就有的。

Handler 构造函数:

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

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

Looper 是通过 Looper.myLooper() 获取到的。

* Looper 的 myLooper:*


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

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

sThreadLocal 是一个 ThreadLocal,ThreadLocal 是用来进行线程数据隔离,保证各个线程中 ThreadLocal.get() 获取到的是当前线程的对象。

在定义 sThreadLocal 上面有个备注,调用 ThreadLocal.get() 之前必须要先调用 prepare() 方法,否则将会返回 null。

Looper 的 prepare:

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

sThreadLocal 的 set 是在 prepare 里面被调用的,即在这里完成 Looper 的初始化。

在 Looper 的 loop()方法中有使用到 Looper,所以 loop()方法肯定是在 prepare()之后调用

 public static void loop() {
        final Looper me = myLooper();
        ...
 }

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

loop()方法跟 prepare()方法的调用是在 ActivityThread (安卓程序的入口) 中的 main 函数被调用。

ActivityThread 的 main:

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

Looper 的 prepareMainLooper:

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

ActivityThread 的 main 先调用了 Looper 的 prepareMainLooper,在这里调用了 Looper 的 prepare 方法,然后再调用了 Looper 的 loop 方法。

注:主线程创建的时候调用 Looper 的 prepare 方法,则创建了第一个 Looper,即安卓程序至少有一个 Looper。

Looper 有个 MessageQueue 的成员变量,这个在构造函数中初始化的,Handler 中的 MessageQueue 直接获取的是 Looper 中的,两个指的是同一个对象。

二、手写实现

根据上面的源码分析进行简单的手写实现 Handler 机制,这边不需要新建安卓项目,直接 java 项目即可。

1.Message

public class Message {

    public int what;
    public Object object;
    Handler target;

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return object.toString();
    }
}

Message 只存储三个数据,what、object 和 Handler,在安卓的 Handler 下 Message 还有进行复用机制的处理,可以一定程度上减小内存的损耗,优化性能。

2.Handler

public class Handler {

    //轮询器
    final Looper mLooper;
    //消息队列
    final MessageQueue mQueue;

    public Handler() {
        //获取当前线程的 Looper
        this.mLooper = Looper.myLooper();
        this.mQueue = mLooper.mQueue;       
    }

    /**
     * 发送消息,压入队列
     */
    public void sendMessage(Message msg) {
        msg.target = this;
        mQueue.enqueueMessage(msg);
    }

    public void dispatchMessage(Message msg) {
        handleMessage(msg);
    }
    public void handleMessage(Message msg) {
    }
}

Handler 拥有两个属性,Looper 和 MessageQueue,根据源代码的实现,Looper 和 MessageQueue 在构造函数的时候初始化,Looper 为当前线程的 Looper(这个会在 Looper 这个类体现),MessageQueue 获取的就是 Looper 下的 MessageQueue 。

同时,还提供了主要调用的方法 sendMessage,和 Looper 出栈后消息回调的方法 handleMessage。在入栈的时候指定消息所属的 Handler 为当前 Handler。

3.Looper

public final class Looper {

    //每一个线程都有一个 Looper
    //Looper 对象保存在 ThreadLocal 中,保证线程数据隔离
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    //一个 Looper 有一个消息队列
    final MessageQueue mQueue;

    private Looper() {
        mQueue = new MessageQueue();
    }

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

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

    /**
     * 轮询消息队列
     */
    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) {
                 continue;
             }

             msg.target.dispatchMessage(msg);
        }

    }
}

Looper 使用 ThreadLocal 进行 Looper 的线程数据隔离,拥有一个 MessageQueue 属性,在构造函数的时候初始化。同时,Looper 在方法 prepare 中进行初始化,获取到当前线程的 Looper 只能通过方法 myLooper 进行获取。

核心代码是 loop 方法,先获取当前线程的 Looper,并判断不为空,保证在调用 loop 之前必须要调用 prepare 方法。然后就是一个死循环,不停的获取消息队列的下一个消息进行转发给 Handle 进行处理。

4.MessageQueue

public class MessageQueue {

    //这边采用数组方式进行存储消息
    Message[] mMessages;

    //存放的索引
    int putIndex;
    //获取的索引
    int getIndex;
    //消息的个数
    int count;

    //互斥锁
    private Lock lock;
    //条件变量
    private Condition noEmpty;
    private Condition noFull;

    public MessageQueue() {
        //这边写死大小
        this.mMessages = new Message[50];

        this.lock = new ReentrantLock();
        this.noEmpty = this.lock.newCondition();
        this.noFull = this.lock.newCondition();
    }

    /**
     * 加入队列
     * @param msg 
     */
    public void enqueueMessage(Message msg) {
        try {
            lock.lock();
            //消息队列满了,子线程停止发送消息,阻塞
            while (count == mMessages.length) {
                try {
                    noFull.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            mMessages[putIndex] = msg;
            //等于 50,置 0
            putIndex = (++putIndex == mMessages.length) ? 0 : putIndex;
            count ++;

            //添加了消息,通知 Looper 进行轮询
            noEmpty.signal();

        } finally {
            lock.unlock();
        }
    }

    /**
     * 出队列
     */
    public Message next() {
        Message msg;
        try {
            lock.lock();
            //消息队列为空,子线程 Looper 停止轮询,阻塞
            while (count == 0) {
                try {
                    noEmpty.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            msg = mMessages[getIndex];
            //取完之后,消息置空
            mMessages[getIndex] = null;
            //等于 50,置 0
            getIndex = (++getIndex == mMessages.length) ? 0 : getIndex;
            count --;           

            //消费了消息,通知 Handler 可以继续添加
            noFull.signal();

        } finally {
            lock.unlock();
        }
        return msg;
    }
}

MessageQueue 跟源码的实现有点不一样。这边消息的存储使用一个数组进行实现,源码使用的是一个链表的结构实现 Message。

为了避免消息无限制的添加,内存不停增大,对消息队列大小进行限制,这边写死为50。

提供了 enqueueMessage 和 next 两个方法,分别实现了消息的入队和出队功能。按消息数组索引进行循环存储和读取。

考虑到两种特殊的情况:1.消息队列满了,子线程停止发送消息。2.消息队列为空,子线程 Looper 停止轮询。这两种情况下需要进行线程的堵塞,等待消息的入队和出队,使用了一个互斥锁进行管理实现。

5.HandleTest

public class HandleTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        final Random random = new Random();

        //轮询器初始化
        Looper.prepare();

        //Handler 存放于主线程
        final Handler handler = new Handler(){

            @Override
            public void handleMessage(Message msg) {
                System.out.println(Thread.currentThread().getName()
                        + "received " + msg.toString());
            };
        };

        //在子线程进行调用 Handler
        for (int i = 0; i < 10; i ++){
            new Thread(){

                @Override
                public void run() {
                    while(true){
                        Message msg = new Message();
                        msg.what = 1;
                        msg.object = Thread.currentThread().getName()
                                + "send message " + random.nextInt(100);
                        System.out.println(msg);

                        handler.sendMessage(msg);

                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                };
            }.start();
        }

        //轮询器开启轮询
        Looper.loop();
    }
}

这是带 main 函数的类,入口,模拟 ActivityThread,先调用 Looper.prepare() ,再调用 Looper.loop(),中间进行新建子线程发送消息。可以在结果中看到,对应的 sendMessage 方法发送消息都有对应的 handleMessage 方法接收到并处理。

结果:
这里写图片描述

三、附

代码链接:http://download.csdn.net/download/qq_18983205/10237523

猜你喜欢

转载自blog.csdn.net/qq_18983205/article/details/79232128