教你手写android底层Handler消息机制实现框架

首先看下android底层Handler消息机制的大致流程:

1、发送消息:Handler——》sendMessage——》MessageQueue——》enqueueMessage (将消息添加到队列)

2、处理消息:Loop——》MessageQueue——》next() (使用消息)——》dispatchMessage——》handleMessage

还需要知道:Handler中使用了生产者-消费者模式

生产者:往队列添加消息,当队列满的时候,不能再添加消息了,此时block(即消息阻塞了),当生产一个消息的时候,通知消费者有消息可以消费了。

消费者:从队列消费消息(获取),如果队列为空,则block,当消费者消费了一个消息的时候,通知生产者可以再生产消息。

下面开始一步一步结合android源码进行分析:

1、发送消息流程分析:

首先平时创建Handler实例对象时基本都是继承Handler或者直接new Handler(),因此先看无参构造函数Handler()。

源码:

    /**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    public Handler() {
        this(null, false);
    }

然后进入this方法:

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

到这里知道了handler实例构造过程,这里面有四个参数mLooper、mQueue、mCallback、mAsynchronous可以先不管,

得到handler对象后再看sendMessage()方法:

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

再看sendMessageDelayed方法:

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


接着sendMessageAtTime方法,MessageQueue queue = mQueue;语句用到了刚构造参数中的mQueue变量:

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


再进入enqueueMessage,这里就看到了其实sendMessage方法最终调用了MessageQueue的enqueueMessage方法,并且注意这句msg.target = this的赋值,后面会看到分析这个参数的作用非常重要的:

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

到这里会注意到这四个参数mLooper、mQueue、mCallback、mAsynchronous中,我们只需要关注前两个参数,后面两个这里就不分析了,本文只分析Handler消息机制的基本流程并简单的实现其消息机制框架来帮助小伙伴们加深理解。

到这里就有两个地方需要了解了:
(1)、mLooper = Looper.myLooper()做了什么,大致猜下肯定是返回了Looper消息循环的实例对象;

(2)、MessageQueue的enqueueMessage方法做了什么处理。

好了,先处理第一个问题,看源码:

    /**
     * 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();
    }
耶?!sThreadLoacl.get()这里是个啥玩意儿,再看这个变量sThreadLocal是个啥:
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 
再看这里变量都在哪里使用了怎么使用的,发现除了get()那里使用了,就只有这里:
    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));
    }
哦这下应该明白了吧,原来是set了一个新的Looper实例,那肯定get到的就是这个Looper对象了。但是不是还有一点没懂,那就是ThreadLocal到底是个啥,就得从set和get进去看下了,这里就不详细分析这个类的具体处理问题了,感兴趣的可以自己去深入研究下,这里就简单提下这个类的具体作用描述下: 
a、ThreadLocal类用来提供线程内部的局部变量。
 这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量
 提供线程内部的局部变量,在本线程内随时随地可取,隔离其他线程
b、 ThreadLocal解决多线程的并发问题
 每个线程维护一个 ThreadLocalMap 的映射表,
 映射表的 key 是 ThreadLocal 实例本身,value 是要存储的副本变量。
 ThreadLocal 实例本身并不存储值,它只是提供一个在当前线程中找到副本值的 key。
c、 每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,
value是真正需要存储的Object。
 
那么大致也就明白了其作用是解决多线程下各个线程的变量相对独立于其他线程内的变量(即这里的变量可以具体指的是Looper对象的实例在同一个线程中只能存在一个值,保证其唯一性与独立性)
 
那么到此第一个问题myLooper的问题解决了,又引出了另一个问题:
(3)、Looper的prepare方法啥时候调用的呢;
 
那就继续第三个问题解决了吧,第二个问题先放着
跟进代码追踪发现有这样一块代码:
    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
大致的英文注解意思:初始化当前线程的一个Looper,使其成为一个应用的主消息循环(其实就是主线程的),你的应用这个主消息循环在安卓运行环境时就被创建了,因此你不应该自己去调用这个方法。
其实大概意思就是这个方法我们不要去调用,只能系统去调用,并且后面的see可以看到prepare方法才是我们需要调用的方法,并且这里有个boolean类型的参数quitAllowed,这个参数大概意思是true的时候是允许退出消息循环,并且还可以标识为用户自己创建的消息循环。
相信稍微看过各个大佬写的分析App怎么被创建的android运行环境,都知道有个重要的App启动入口即ActivityThread的main()方法(并且这里的英文注释也有提到),也就相当于java的main方法才能启动程序一样。那么我们跟进这个方法里面去可以看到:
public static void main(String[] args) {
        // 去除多余的代码,只看关键地方.......

        Looper.prepareMainLooper();
	// 多余的代码......
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }多余的代码......
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

现在就明白了吧,原来是App启动的时候就会在App内部创建一个主消息循环来进行消息处理,并且无限循环来处理消息。
第三个问题也解决了,很不巧又引出了另一个不懂的地方:
(4)、Looper.loop()这是个什么呢。一会儿会讲到其实就是主消息循环开始工作即无限循环来分发处理各个消息并分析原理,这里先讲发送消息部分。
 
好了,现在来分析发送消息的第二个问题:MessageQueue的enqueueMessage方法做了什么处理,首先说一下MessageQueue的数据结构,是一个单向链表,Message对象有个next字段保存列表中的下一个,MessageQueue中的mMessages保存链表的第一个元素即当前排在最前面的待处理Message。
其实看方法名大致明白该方法应该是添加消息到队列中,这里是重点,看源码:
// 首先如果从这段代码中可以看出:你可以将消息队列看成是一个根据时间的先后顺序来排序的队列。
// 这点非常重要才有助于理解,由于本文不打算实现这个这个延迟功能,因此只会简单的分析涉及到的延迟操作逻辑
boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // ...
	// 这里使用synchronized关键字来保证线程安全
        synchronized (this) {
            // ...
	    // 关键点【1】 when其实就是需要延迟发送消息的时间
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
		// 这里的意思是该消息循环中第一个消息(即p==null时)或者不需要延迟的消息(when==0)
		// 或者当前加入队列的消息msg比当前排在最前面的那个待处理的消息的延迟时间还要短(when<p.when)
                // New head, wake up the event queue if blocked.
                msg.next = p;
		// mMessages其实就是当前最前面的第一个需要处理消息	
                mMessages = msg;
		// mBlocked参数的意思就是消息阻塞,注意:改值赋值为true时是在消费者那里消费时队列为空时或者等待轮询一次的时候才赋值为空(即next()方法处)
                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) {
		// 关键点【2】当消息阻塞的时候,需要唤醒消息循环,其实就是生产者发现消息循环被阻塞了肯定要唤醒啦
                nativeWake(mPtr);
            }
        }
        return true;
    }
代码中已给了基本的一些阐述,由于本文实现的消息机制框架不考虑实现延迟操作,又因为唤醒操作实在native层,因此延迟操作和阻塞原理只是简单的提了一下,若需要深入研究可能需要自行分析源码以及native层的源码吧。
到这里我们就基本明白了sendMessage其实就是将消息添加到消息队列中并且按时间先后排序,并且根据消息是否阻塞决定是否唤醒消息循环:阻塞了就需要唤醒。
 
该方法主要就是将Message加入链表的时候按时间先后顺序排序,而后判断是否需要唤醒,如果需要唤醒则调用nativeWake(mPtr);语句来唤醒之前等待的线程。
 
消息发送流程基本分析好了,接下来:
2、处理消息流程分析:
还记得上面留了个问题:Looper.loop()吧,没错真正的消息开始循环是从这里开始的,而我们平时使用handleMessage()方法处理的消息来源也是从这里轮询进行获取并分发消息过来的,先看源码:
   // 消息循环开始工作
    public static void loop() {
	// 这里myLooper()之前讲过就是获取了当前线程中使用的唯一的Looper,
	// 面试可能会问:Looper是运行在哪个线程中的呢?其实每个线程都对应一个Looper即消息循环,因此你可以在线程中创建一个Looper,所以Looper是运行在多线程中的。
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
	// 直接使用了Looper消息循环中的消息队列,保证了数据的一致性,因为一个线程肯定只会有一个消息循环Looper那么肯定也只有一个消息队列MessageQueue
        final MessageQueue queue = me.mQueue;

        // ...

	//然后开始无限循环进行消息处理
        for (;;) {
	    // 关键点【1】获取下一个消息用来处理,可能会被阻塞,阻塞的问题刚在上面进行了简单的分析
            Message msg = queue.next(); // might block,从MessageQueue获取待处理的Message(阻塞线程)
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // ...
            try {
		// 关键点【2】这里非常关键,target我们前面讲过是Handler调用者自身,因此肯定是调用的自身dispatchMessage()
                msg.target.dispatchMessage(msg);
                // ...
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            } // ....
            msg.recycleUnchecked();// 回收Message,供Message.obtain()复用.
        }
    }
代码中已经有了一些标注讲解,剩下的就只需要分析两个关键点的地方了,OK,我们开始看关键点【1】queue.next()方法如何获取的呢,请看源码,看主要部分,后面省略了一部分IdleHandler的处理逻辑,用于空闲的时候处理不紧急事件用的,有兴趣的自行分析:
// 这个方法的代码有点多,我们只看关键点就可以了,这里也只是简单分析下阻塞的情况,
Message next() {
        // ...
	// 只有第一次循环时pendingIdleHandlerCount 才会为-1,这个变量就不详细讲了
        int pendingIdleHandlerCount = -1; 
	// 下次循环的延迟时间即第一个需要处理的Message的延迟时间
        int nextPollTimeoutMillis = 0;
        for (;;) {
            // ...
	    // 这也是个native方法,大概的作用是根据延迟时间nextPollTimeoutMillis,来决定延迟多久开始再次循环,或者-1时没有消息会阻塞线程,其实就是通过Native层的MessageQueue阻塞nextPollTimeoutMillis毫秒的时间,这里不做过多分析,感兴趣可自行去了解下怎么做到的
            nativePollOnce(ptr, nextPollTimeoutMillis);
	   
  	    // 保证线程安全
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
		// 关键点【1】 耶,这里的mMessages不就是在enqueueMessage()方法中经过排序消息后第一个需要处理的消息嘛,这下就明白了吧 
                Message msg = mMessages;
                // ...
                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 {
                        // 否则的话立刻执行返回本次需要处理的Message消息,并且赋值mBlocked为false即没有阻塞了
                        mBlocked = false;
                        if (prevMsg != null) {// 平常发送的一般都是同步消息,这里不分析这种情况,可自行分析
			    // prevMsg值只有在异步Message的时候才不会为空,因此这一步我们不分析考虑,有兴趣可以自行看源码了解
                            prevMsg.next = msg.next;
                        } else {
			   // 将需要处理下一个Message赋值给缓存的下次需要处理的mMessages,使其下次可以继续执行
                            mMessages = msg.next;
                        }
			// 清空该消息后面所有的Message消息对象,这个肯定啦,本次的消息都要进行处理了就需要将后面的隔开咯
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
			// 最后返回需要处理的Message消息咯
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

               	// ...

                if (pendingIdleHandlerCount <= 0) {
		    // 这里消息队列为空或者待处理的消息未到执行时间,就block阻塞住,等待下一次有消息进来时唤醒消息循环
                    mBlocked = true;
                    continue;
                }

               // ...
            }

            // ...

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // 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;
        }
    }
 
源码中已经做了分析,可以看出:阻塞nextPollTimeoutMillis毫秒时间的值不同的带来的不同作用即:
(1)nextPollTimeoutMillis=-1,一直阻塞不会超时;
(2)nextPollTimeoutMillis=0,不会阻塞并且立即返回待处理msg;
(3)nextPollTimeoutMillis>0,线程最长阻塞的nextPollTimeoutMillis毫秒数时间,并且若有程序唤醒就会立刻返回(添加消息到队列时就有唤醒的操作)。
因此消息循环处理消息时:
一、如果有消息需要处理,先判断时间有没有到,如果没到的话设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis);来进行阻塞,等待阻塞时间到或者程序唤醒。
二、否则把消息返回给调用者,并且设置mBlocked = false代表目前没有阻塞。
 
关于怎么阻塞的感兴趣的话请自行去了解native层源码咯。
 
OK,到这里已经分析完处理消息中获取消息的机制了,接下来最后一个问题就是loop()方法中获取到了msg后会调用msg.target.dispatchMessage(msg),来进行消息的分发给调用者自行处理消息,源码如下:
    // 分发消息,代码很简单
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
	    // msg的callback变量,我们可以在Message数据结构中找到是一个Runnable对象,
	    // 最后追踪过去会发现handleCallback方法调用了message.callback.run();即Runnable的run方法呢
	    // 那么你可能会问,这个变量到底干啥的,好,我们看下msg的callback这个变量到底什么时候赋值的呢,
	    // 可以发现Handler几个postXXX方法看到该Runnable的赋值例如post(Runnable r)、postDelayed(Runnable r, long delayMillis)
	    // 耶,这下是不是很明白了平时我们调用的Handler.postXXX方法的最终流程了吧哈哈哈,并且这些psotXX最终也会调用MessageQueue.enqueueMessage()方法哦哈哈
            handleCallback(msg);
        } else {
            if (mCallback != null) {
		// 这里变量其实你可以在Handler的有参构造方法中发现这样的语句:mCallback = callback;,没错,就是你自己使用了一个Callback实例对象进行了处理
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
	    // 关键点【1】这句就是我们平时复写的处理消息的方法啦哈哈
            handleMessage(msg);
        }
    }
代码中已做了详细分析,其实你若是再进入handleMessage方法中去看,你会发现这个方法再Handler中啥事都没做,其实就是需要让我们自行处理的啦。
 
啦啦啦♪(^∇^*),源码分析到此结束,好久没有写文章了,哎,没什么文采不要见怪哈。
 
接下来就是本文的目的:手写android底层Handler消息机制实现框架的一个简易版本
好啦,直接开始撸码,经过源码的分析,并且实现的代码中已经有了详细的注释,就不在多余的阐述了吧。
直接上代码:
1、Message类:
package com.lh.myhandlerlib;

/**
 * 消息Message
 *
 * @author LuoHao
 *         Created on 2018/2/25 13:28
 */

public class Message {
    /**
     * 需要发送的Handler
     */
    public Handler target;

    public String what;

    public Message(String what) {
        this.what = what;
    }

    @Override
    public String toString() {
        return "what: " + what;
    }
}

2、消息队列类MessageQueue:
package com.lh.myhandlerlib;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * 简易的消息队列
 * 没有延时操作等,这里只是实现其主要思路
 * 延时操作源码里面是通过当前时间与延迟的对比处理的,可自行查阅源码
 *
 * @author LuoHao
 *         Created on 2018/2/25 13:28
 */
public class MessageQueue {

    /**
     * 消息队列
     */
    private static final int COUNT = 50;

    /**
     * 使用阻塞队列BlockingQueue解决生产者消费者
     * <p>
     * 主要作用是效仿android源码native层的唤醒和堵塞消息队列的问题
     * android源码使用的native方法nativePollOnce(阻塞),nativeWake(唤醒),这里使用另一种java并发提供的阻塞队列类替换
     */
    private final BlockingQueue<Message> mMessageQueue;

    public MessageQueue() {
        mMessageQueue = new ArrayBlockingQueue<>(COUNT);
    }

    /**
     * enqueue a message
     * <p>
     * should be called in {@link Handler#sendMessage(Message)}
     *
     * @param msg message
     * @author LuoHao
     * Created on 2018/2/26 17:20
     */
    public void enqueueMessage(Message msg) {
        try {
            mMessageQueue.put(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取下一个Message对象
     * <p>
     * 消息循环时读取
     *
     * @author LuoHao
     * Created on 2018/2/25 14:33
     */
    public Message next() {
        try {
            return mMessageQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

3、消息循环类Looper:
package com.lh.myhandlerlib;

/**
 * 简易的消息循环Looper
 *
 * @author LuoHao
 *         Created on 2018/2/25 13:28
 */
public class Looper {

    /**
     * 消息队列
     */
    public MessageQueue mQueue;

    /**
     * ThreadLocal类用来提供线程内部的局部变量。
     * 这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量
     * 提供线程内部的局部变量,在本线程内随时随地可取,隔离其他线程
     * <p>
     * ThreadLocal解决多线程的并发问题
     * 每个线程维护一个 ThreadLocalMap 的映射表,
     * 映射表的 key 是 ThreadLocal 实例本身,value 是要存储的副本变量。
     * ThreadLocal 实例本身并不存储值,它只是提供一个在当前线程中找到副本值的 key
     * <p>
     * 每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,
     * value是真正需要存储的Object
     */
    private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();

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

    /**
     * Lopper 初始化
     *
     * @author LuoHao
     * Created on 2018/2/25 14:04
     */
    public static void prepare() {
        if (sThreadLocal.get() != null) {
            sThreadLocal.remove();
            throw new RuntimeException("Only one Looper may be created per Thread");
        }
        sThreadLocal.set(new Looper());
    }

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

    /**
     * 提供系统动力,让消息队列开始循环工作
     *
     * @author LuoHao
     * Created on 2018/2/25 14:07
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            sThreadLocal.remove();
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this Thread");
        }
        final MessageQueue queue = me.mQueue;
        // 死循环开始遍历消息并分发
        for (; ; ) {
            // might block
            Message msg = queue.next();
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                System.out.println(" No message indicates that the message queue is quitting.");
                return;
            }
            msg.target.dispatchMessage(msg);
        }
    }
}
4、消息处理类Handler:
package com.lh.myhandlerlib;

/**
 * 简易的消息处理Handler
 * <p>
 * 手写android 底层Handler机制实现框架
 *
 * @author LuoHao
 *         Created on 2018/2/25 13:28
 */
public class Handler {

    /**
     * 消息队列
     */
    private final MessageQueue mQueue;

    /**
     * 消息循环
     */
    private final Looper mLooper;

    public Handler() {
        this(Looper.myLooper());
    }

    public Handler(Looper looper) {
        mLooper = looper;
        // 保证MessageQueue与Looper对应性
        mQueue = mLooper.mQueue;
    }

    /**
     * 发送消息
     *
     * @param msg message
     * @author LuoHao
     * Created on 2018/2/26 16:40
     */
    public final void sendMessage(Message msg) {
        // target是Handler本身
        // 多线程中保证Handler的一致性,并且这句是在主线程中调用的,
        // 当其调用自身的handleMessage()时才会有线程切换的效果
        msg.target = this;

        // enqueue a message
        mQueue.enqueueMessage(msg);
    }

    /**
     * 复写该方法 处理消息
     *
     * @author LuoHao
     * Created on 2018/2/26 16:44
     */
    public void handleMessage(Message msg) {

    }

    /**
     * 面向对象设计模式
     * 责任链调度原则
     *
     * @param msg message
     */
    public void dispatchMessage(Message msg) {
        handleMessage(msg);
    }
}
5、最后肯定需要测试的啦,Handler框架测试类: 测试类中有手动创建线程池规范及其使用,不太了解的也需要自行了解一下多线程和线程池哦
package com.lh.myhandlerlib;

import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 手写android 底层Handler机制实现框架的测试
 *
 * @author LuoHao
 *         Created on 2018/2/26 16:27
 */
public class HandlerFrameTest {

    /**
     * 线程数量
     */
    private static final int THREAD_COUNT = 10;

    private static ThreadFactory namedThreadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "common-thread-pool");
        }
    };

    /**
     * Common Thread Pool
     * <p>
     * 手动创建线程池 建议不直接使用Executors.newCachedThreadPool()
     */
    private static ExecutorService commonThreadPool = new ThreadPoolExecutor(5, 200,
            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024),
            namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    private static ThreadFactory namedThreadFactory2 = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "single-thread-pool");
        }
    };

    /**
     * Single Thread Pool
     * <p>
     * 手动创建线程池代替直接显示new Thread的问题
     */
    private static ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1
            , 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024)
            , namedThreadFactory2, new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        // 手写android 底层Handler机制实现框架
        Looper.prepare();

        final Handler myHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                System.out.println("handle msg: thread:" + Thread.currentThread().getId() +
                        " Msg: " + msg.toString());
            }
        };

        // 开启多线程
        singleThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("singleThreadPool function thread name: " +
                        Thread.currentThread().getName());
                for (int i = 0; i < THREAD_COUNT; i++) {
                    commonThreadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println("commonThreadPool function thread name: " +
                                    Thread.currentThread().getName());
                            Message msg = new Message(UUID.randomUUID().toString());
                            System.out.println("send Msg: thread: " + Thread.currentThread().getId() +
                                    " Msg: " + msg.toString());
                            myHandler.sendMessage(msg);
                        }
                    });
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        // new Thread().start()

        Looper.loop();

        // 异常
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

}
 
测试结果:每个线程都休眠1s
singleThreadPool function thread name: single-thread-pool
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 12 Msg: what: ac2fdf80-2e8e-42fc-b48b-d62bde634f97
handle msg: thread:1 Msg: what: ac2fdf80-2e8e-42fc-b48b-d62bde634f97
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 13 Msg: what: 0438c263-adbf-43f8-927c-9432166b10b8
handle msg: thread:1 Msg: what: 0438c263-adbf-43f8-927c-9432166b10b8
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 14 Msg: what: 1cbb5240-932e-4dcc-9e5e-7dfdd7bc3bfd
handle msg: thread:1 Msg: what: 1cbb5240-932e-4dcc-9e5e-7dfdd7bc3bfd
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 15 Msg: what: c340722a-4d66-4620-8458-86606c07e496
handle msg: thread:1 Msg: what: c340722a-4d66-4620-8458-86606c07e496
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 16 Msg: what: ec749fa9-b9f6-4104-a149-a418e9426435
handle msg: thread:1 Msg: what: ec749fa9-b9f6-4104-a149-a418e9426435
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 12 Msg: what: 5093039f-be23-41c9-9666-2f0a11225918
handle msg: thread:1 Msg: what: 5093039f-be23-41c9-9666-2f0a11225918
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 13 Msg: what: 24c8b5fe-bbaa-48d5-8515-39f638f90f2b
handle msg: thread:1 Msg: what: 24c8b5fe-bbaa-48d5-8515-39f638f90f2b
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 14 Msg: what: 1e4973fd-ba4e-4168-9846-c7f893dfb5ec
handle msg: thread:1 Msg: what: 1e4973fd-ba4e-4168-9846-c7f893dfb5ec
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 15 Msg: what: 6e6d350d-13fa-4366-9da2-e3c7d109c302
handle msg: thread:1 Msg: what: 6e6d350d-13fa-4366-9da2-e3c7d109c302
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 16 Msg: what: 9c11acf0-88d9-4983-9680-8f3eb14f2976
handle msg: thread:1 Msg: what: 9c11acf0-88d9-4983-9680-8f3eb14f2976
 
测试结果:每个线程都不休眠
singleThreadPool function thread name: single-thread-pool
commonThreadPool function thread name: common-thread-pool
commonThreadPool function thread name: common-thread-pool
commonThreadPool function thread name: common-thread-pool
commonThreadPool function thread name: common-thread-pool
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 14 Msg: what: 610e827c-e909-44af-8196-e158e26a9e80
send Msg: thread: 15 Msg: what: f3c5976f-f297-4840-b474-84e660dde2b2
send Msg: thread: 12 Msg: what: 600de1c3-a523-4079-bf2e-e6481e848c6c
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 12 Msg: what: 69719d30-eabc-4774-83dc-fc3b3c969579
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 12 Msg: what: 2326cc73-66cc-4d73-8c05-045542014824
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 12 Msg: what: a39c84da-5318-4084-bbf2-e6a1e2d6b09b
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 12 Msg: what: 4e2e8724-abc7-475d-b4f6-1c8dec91a21d
handle msg: thread:1 Msg: what: f3c5976f-f297-4840-b474-84e660dde2b2
handle msg: thread:1 Msg: what: 600de1c3-a523-4079-bf2e-e6481e848c6c
handle msg: thread:1 Msg: what: 69719d30-eabc-4774-83dc-fc3b3c969579
handle msg: thread:1 Msg: what: 2326cc73-66cc-4d73-8c05-045542014824
send Msg: thread: 16 Msg: what: 5350e3de-da0f-4d28-b75f-f83e388906c0
send Msg: thread: 13 Msg: what: b6f776f3-18ab-4cc2-b247-4ccf10718841
commonThreadPool function thread name: common-thread-pool
send Msg: thread: 15 Msg: what: dbb4db1f-0649-459a-9142-c7c9fdedc828
handle msg: thread:1 Msg: what: a39c84da-5318-4084-bbf2-e6a1e2d6b09b
handle msg: thread:1 Msg: what: 4e2e8724-abc7-475d-b4f6-1c8dec91a21d
handle msg: thread:1 Msg: what: 610e827c-e909-44af-8196-e158e26a9e80
handle msg: thread:1 Msg: what: 5350e3de-da0f-4d28-b75f-f83e388906c0
handle msg: thread:1 Msg: what: b6f776f3-18ab-4cc2-b247-4ccf10718841
handle msg: thread:1 Msg: what: dbb4db1f-0649-459a-9142-c7c9fdedc828
测试代码中,只测试了10个线程,当然你可以测试10000个或者更多,这时候就可以把休眠给去掉再看看效果啦,我测了下10000个,能够看到的打印的日志看了下都能一一对应呢,可自行测试下。
 
由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出,谢谢大家:)
 
如果对你有所帮助,各位大大给个赞吧,也希望以后会有更好的文章技术分享给大家,╰( ̄ω ̄o)下次见面啦。

猜你喜欢

转载自blog.csdn.net/u012430727/article/details/79382042