必备的系统知识#Handler

44 阅读12分钟

必备的系统知识#系统启动

必备的系统知识#AMS

必备的系统知识#PMS

必备的系统知识#Binder

必备的系统知识#Apk打包签名

必备的系统知识#Handler

一.什么是Handler?

1.如何在子线程更新UI?

1.在viewRootImpl初始化之前更新

我们知道ViewRootImpl是在onResume之后初始化完成,view也没有被attacthtoWindow,所以也不会去检测线程,那下面两种都可以更新UI,如果在线程中做个几百毫秒的延时,就会做线程检测,但是也不完全,比如TextView如果设置了固定的高度,我们参考源码就可以看到,它在调用重绘后就结束了

    TextView textView;
    ImageView img;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);
        img = findViewById(R.id.img);
        new Thread(new Runnable() {
            @Override
            public void run() {

//                textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
                textView.setLayoutParams(new LinearLayout.LayoutParams(20, 20));
                textView.setText("onCreate");


                /**        textView.getLayout()获取的是空
                 *       ...
                 *      if (mLayout != null) {
                 *             checkForRelayout();
                 *         }
                 *      ...
                 */
                System.out.println("attach:"+textView.isAttachedToWindow());
                System.out.println("parent:"+textView.getParent());
                System.out.println("layout:"+textView.getLayout());

            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

               /*
                  只要TextView设置的为固定值 依然可以更新

                private void checkForRelayout() {
                    // If we have a fixed width, we can just swap in a new text layout
                    // if the text height stays the same or if the view height is fixed.

                    if ((mLayoutParams.width != ViewGroup.LayoutParams.WRAP_CONTENT
                            || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
                            && (mHint == null || mHintLayout != null)
                            && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
                        // Static width, so try making a new text layout.

                        int oldht = mLayout.getHeight();
                        int want = mLayout.getWidth();
                        int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();

                        *//*
                         * No need to bring the text into view, since the size is not
                         * changing (unless we do the requestLayout(), in which case it
                         * will happen at measure).
                         *//*
                        makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
                                mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
                                false);

                        if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
                            // In a fixed-height view, so use our new text layout.
                            if (mLayoutParams.height != ViewGroup.LayoutParams.WRAP_CONTENT
                                    && mLayoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
                                autoSizeText();
                                invalidate();
                                return;
                            }

                            // Dynamic height, but height has stayed the same,
                            // so use our new text layout.
                            if (mLayout.getHeight() == oldht
                                    && (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
                                //走到这里return 了
                                autoSizeText();
                                invalidate();
                                return;
                            }
                        }

                        // We lose: the height has changed and we have a dynamic height.
                        // Request a new view layout using our new text layout.
                        requestLayout();
                        invalidate();
                    } else {
                        // Dynamic width, so we have no choice but to request a new
                        // view layout with a new text layout.
                        nullLayouts();
                        requestLayout();
                        invalidate();
                    }
                }*/
                textView.setText("onCreate2");
                System.out.println("attach222:"+textView.isAttachedToWindow());
                System.out.println("parent222:"+textView.getParent());
                System.out.println("layout222:"+textView.getLayout());
            }
        }).start();

    }

    @Override
    protected void onResume() {
        super.onResume();
        new Thread(new Runnable() {
            @SuppressLint("SetTextI18n")
            @Override
            public void run() {
                /**
                 *        final AttachInfo ai = mAttachInfo;
                 *             final ViewParent p = mParent;
                 *             if (p != null && ai != null && l < r && t < b) {
                 *                 final Rect damage = ai.mTmpInvalRect;
                 *                 damage.set(l, t, r, b);
                 *                 p.invalidateChild(this, damage);
                 *             }
                 */
                img.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                img.setImageResource(R.drawable.ic_launcher_background);
                System.out.println("attach333:"+img.isAttachedToWindow());
                System.out.println("parent333:"+img.getParent());
            }
        }).start();

2.surfaceview

这个就不用说了,独立于view,有自己单独的surface。

3.toast

在子线程中显示前,调用下Looper.prepare(),后面开启looper循环即可,这个可以查看源码。下面贴下使用,源码部分就不贴了,也很简单,就是它会做下looper检测,现实的时候会通过handler将现象发送出去,中间还有进程间通讯,这里就不展开了。

new Thread(new Runnable() {
    @Override
    public void run() {

        try {
            Thread.sleep(1_000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        Looper.prepare();
        Toast.makeText(MainActivity.this,"it's toast!!!",Toast.LENGTH_SHORT).show();
        Looper.loop();
    }
}).start();


2.使用handler切换线程

这个就是在子线程中通过handler将消息发送到主线程去更新UI,这个Handler可以是在子线程中创建,但是Looper必须指定主线程中的

  private Handler handler =new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {

            switch (msg.what) {
                case 1:
                    Toast.makeText(MainActivity.this,"handler"+msg.what,Toast.LENGTH_SHORT).show();
                    break;
            }
            return false;
        }
    });


new Thread(new Runnable() {
    @Override
    public void run() {

        //1.通过主线程的Handler发送
        //handler.sendEmptyMessage(1);
        //2.新建一个Handler,传入主线程的Looper更新
        new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 2:
                        System.out.println("handler inner thread used main looper"+ msg.what);
                        Toast.makeText(MainActivity.this,"handler"+msg.what,Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        }.sendEmptyMessage(2);
    }
}).start();

二.Handler以及HandlerThread

1.Handler机制及源码解析

前面我们已经了解到,我们的UI只能在主线程中更新,这个是一个不完全正确的结论,但是正常情况下,我们都会通过主线程去更新UI,所以就绕不开Handler,那我们接下来就分析下Handler的源码及其工作流程,它主要由三大流程,消息的发送、消息队列循环以及消息的分发处理。

1.Handler发送消息流程:

Handler.java
//构造方法
  public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;//我们传过来的是主线程的looper
        mQueue = looper.mQueue; 
        mCallback = callback;
        mAsynchronous = async;
    }

   public final boolean post(Runnable r)
    {
    //把runnable包装成Message对象
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
   private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//注意这个里面是把this,也就是当前Handler给target了,后面消息处理会拿出这个target执行的
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

MessageQueue.java
插入到Message中
  boolean enqueueMessage(Message msg, long when) {
        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) {
                // 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;
    }

2.消息循环

前面已经说到Handler中需要有个Looper对象,当我们收到消息的时候,就会通过Handler中的额Looper中的MessageQueue将消息加入到其中,那我们来看看这个Looper:

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

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. See also: {@link #prepare()}
 *
 * @deprecated The main looper for your application is created by the Android environment,
 *   so you should never need to call this function yourself.
 */
@Deprecated
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

/**
 * Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

我们可以看到Looper并未给我们提供公开的构造方法去创建,而是通过Looper.prepare或looper.prepareManLooper去创建Looper,并且针对每个线程这个looper是唯一的,那创建的MessageQueue对应线程也是唯一 。消息已经发送了并且加入队列了那是怎么

消费的呢?答案还是在Looper中的Looper这个方法中,这个方法会一直循环检测是否由消息,如果由就会丢给Handler执行:


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
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
        }

        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            msg.target.dispatchMessage(msg);//还记得target是什么吗?前面说到的Handler
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (slowDispatchThresholdMs > 0) {
            final long time = end - start;
            if (time > slowDispatchThresholdMs) {
                Slog.w(TAG, "Dispatch took " + time + "ms on "
                       + Thread.currentThread().getName() + ", h=" +
                       msg.target + " cb=" + msg.callback + " msg=" + msg.what);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

3.消息处理



其实我们可以看到,决定我们代码在哪里执行的并非是Handler在哪里创建的,而是Looper在哪里创建的,如果你在主线程创建了Looper,那么这个looper循环消息拿到消息分发给message.target.dispathMessage就是在主线程,如果是在子线程这段消息循环就在子线程中执行,并且Handler可以是任意个,对应线程的Looper以及MessageQueue就只有一个。然后我们在来解析下Handler中的dispatchMessage这个方法:



  public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

private static void handleCallback(Message message) {
        message.callback.run();
    }


public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        //1.postRunnable执行
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //2.传入回调
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //3.执行方法重写
        handleMessage(msg);
    }
}

我们从源码里面可以看到,它首先会判断msg.callBack是否为空,这里的callback其实就是handler.postRunnable中runnable,如果没有就会调用mCallBack,那这个其实就是new Handler(CallBack callBack)中的接口回调,如果你没有传入这个回调,那就执行handlerMessage,也就是你需要重写这个方法。

2.HandlerThread的用法

HandlerThread其实就是一个Thread,只不过它在start方法时,会自动帮助我们手动创建一个looper并且开启looper循环,相比较一般Thread而言,他自带了消息队列,任务按顺序执行,无需关心线程同步, ,并且提供了退出循环的接口。下面我们就来看看它的用法:

private void testHandlerThread(){

    //1.创建后台工作线程,其实就是一个线程
    HandlerThread handlerThread=new HandlerThread("handlerThread1");
    //2.开启looper循环,会创建当前线程的looper,然后循环,会一直循环消息,相比较普通线程的while(xx)
    handlerThread.start();

    //3.new一个Handler,传入HandlerThread中的looper,重写Handler的handlerMessage就可以发送并接受
    //handlerThread中的Message了,并且它是子线程的
    Handler workHandler=new Handler(
        handlerThread.getLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            System.out.println("testHandlerThread---"+msg);
        }
    };
    //4.发送消息
    workHandler.sendEmptyMessage(22);
    //5.使用完后要安全退出,避免内存泄漏
    handlerThread.quitSafely();



}

三.手写Handler

1.消息体

从缓存链表中获取消息体,如果没有则创建一个消息体。

插入缓存,是由MessageQueue操作的,当使用消息时会设置标记位,如果不在使用则将其加入到消息体中的缓存池中:

使用单向链表和复用池,最多可以缓存50个,使用完成后放置对象池中,避免对象的重复创建。

public class MyMessage {

    public MyHandler target;
    public Runnable callBack;

    public int what;

    private int useFlag = 0; //使用标志位
    static final int FLAG_IN_USE = 1 ; //正在使用

    public static final Object sPoolSync = new Object(); //同步锁
    private static MyMessage sPool; //链表头 即当前正在使用的 静态的
    private static int sPoolSize = 0; //当前单向链表长度
    private static final int MAX_POOL_SIZE = 50;//链表最大长度

    private MyMessage next;//当前链表的下一个结点


    /**
     * {@link MyHandler#obtain()}
     *
     * @param myHandler 绑定的回调
     * @return
     */
    public static MyMessage obtain(MyHandler myHandler) {
        MyMessage myMessage = obtain();
        myMessage.target = myHandler;
        return myMessage;
    }

    /**
     * {@link MyHandler#obtain()}
     *
     * @return
     */
    public static MyMessage obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                System.out.println("hit it from cache ---->");
                //说明有数据,返回
                MyMessage myMessage = sPool;//返回当前头部数据
                sPool = myMessage.next;//将当前头部指向头部的下一个结点
                myMessage.what = -1;//清除消息内容
                myMessage.useFlag = 0;//清除使用标记
                sPoolSize--;
                return myMessage;
            }
        }
        //链表中并没有
        return new MyMessage();
    }

    /**
     * 放入链表池 整个退出时 如果正在使用该消息则return
     */
    public void recycle() {
        if (isInUse()) { //是否正在使用 如果使用不许放到回收池中
            return;
        }
        unCheckRecycle();
    }

    /**
     * looper 中遍历过这条消息并且执行过操作了 如msg.target.dispatchMessage()之后
     */
    public void unCheckRecycle() {
        useFlag = FLAG_IN_USE;//放置多次recycle,放入后,当我们通过obtain获取时又会置为
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                System.out.println("recycle it to cache ---->");
                next = sPool;//将当前的next设置成上一个头 这里sPool是上一个头 next是当前message
                sPool = this;//将当前头设置成当前回收的对象
                sPoolSize++;
            }

        }
    }

    /**
     * {@link MyMessage#recycle()} 判断是否在用
     * {@link MyMessageQueue#enqueueMessage(MyMessage)} }
     *
     * @return
     */
    public boolean isInUse() {
        return ((useFlag & FLAG_IN_USE) == FLAG_IN_USE);
    }


    /**
     * {@link MyMessageQueue#enqueueMessage(MyMessage)} } 标记在用
     */
    public void markInUse() {
        useFlag |= FLAG_IN_USE;
    }


}

2.消息队列

存储消息队列,并提供取出和存入消息的接口,当消息取出时,标记已使用,当消息被取出使用后放入消息池中。

public class MyMessageQueue {
    //消息阻塞队列
    private BlockingQueue<MyMessage> myMessages = new ArrayBlockingQueue<>(50);
    MyMessage myMessage;

    private boolean mQuiting = false;


    /**
     * 存入到消息队列
     *  1.判断是否正在使用
     *  2.判断是否退出looper
     *  3.设置使用标记
     * @param myMessage
     */
    public void enqueueMessage(MyMessage myMessage) {
        try {
            if(myMessage.isInUse()){
                //如果正在使用
                System.out.println("msg is using------>>>");
                return;
            }
            if(mQuiting){
                //如果全局退出了,就不再加入到
                System.out.println("queue has quit--->>>");
                myMessage.recycle();
                return;
            }
            myMessage.markInUse();//标记正在使用
            myMessages.put(myMessage);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 取出消息 阻塞队列 直到取到消息
     *
     * @return
     */
    public MyMessage next() {
        try {
            return myMessages.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }


    public void quit() {
        mQuiting = true;
    }
}

3.发动机-Looper

消息循环的发动机,并通过ThreadLocal保存起来,让单个线程只拥有一个发动机,并且提供发动和取消发动的接口。

public class MyLooper {
    private static ThreadLocal<MyLooper> threadLocal = new ThreadLocal();
    private static MyMessageQueue myMessageQueue;


    public static void prepareLooper() {
        prepare();
    }

    private static void prepare() {
        if (threadLocal.get() != null) {
            throw new RuntimeException("my looper is not null can not recreate!!!");
        }
        //当前线程只允许创建1次
        threadLocal.set(new MyLooper());
    }

    private MyLooper() {
        System.out.println("create MyLooper---->>>>>>");
        myMessageQueue = new MyMessageQueue();

    }

    //线程唯一
    public static MyLooper myLooper() {
        System.out.println("getMyLooper---" + threadLocal.get());
        return threadLocal.get();
    }


    //循环消息
    public static void looper() {

        for (; ; ) {
            MyMessage next = myMessageQueue.next();//拿不到消息就会阻塞在这里
            next.target.dispatchMessage(next);
            next.unCheckRecycle();//放到链表池中
        }

    }

    public void quit(){
        synchronized (this){
            myMessageQueue.quit();
        }
    }


    public MyMessageQueue getMessageQueue() {
        return myMessageQueue;
    }
}

4.中间人-Handler

往发动机中发送消息,可以初始化发动机,但是在单个线程中,发动机只能初始化1次,他也是发动机消息执行回调的承载,但是回调执行的线程是由发动机在那个线程创建决定的,并非是完全由Handler决定。

public class MyHandler {

    private final MyLooper myLooper;
    private MyMessageQueue myMessageQueue;

    private CallBack callBack;

    public void handleMessage(MyMessage myMessage) {

    }

    /**
    * 发动机的回调
    */
    public void dispatchMessage(MyMessage next) {
        if (null != next.callBack) {
            next.callBack.run();
        } else {
            if (callBack != null) {
                if (callBack.handlerMessage(next)) {
                    return;
                }
            }
            handleMessage(next);
        }
    }

    public interface CallBack {
        boolean handlerMessage(MyMessage myMessage);
    }


    public MyHandler() {
        this(null, MyLooper.myLooper());
    }

    public MyHandler(CallBack callBack) {
        this(callBack, MyLooper.myLooper());
    }

    public MyHandler(MyLooper myLooper) {
        this(null, myLooper);
    }

    //在xx个线程使用handler前,需要确保该线程已经创建了looper
    public MyHandler(CallBack callBack, MyLooper looper) {
        if (looper == null) {
            System.out.println("");
            this.myLooper = MyLooper.myLooper();
        } else {
            this.myLooper = looper;
        }
        if (myLooper == null) {
            throw new RuntimeException(
                "Can't create MyHandler inside thread " + Thread.currentThread()
                + " that has not called Looper.prepare()");
        }
        this.callBack = callBack;
        myMessageQueue = myLooper.getMessageQueue();

    }

    public void post(Runnable runnable) {
        MyMessage myMessage = new MyMessage();
        myMessage.callBack = runnable;
        sendMessage(myMessage);
    }

    public void sendMessage(MyMessage myMessage) {
        myMessage.target = this;
        myMessageQueue.enqueueMessage(myMessage);
    }

    /**
     * 提供获取消息的api
     *
     * @return
     */
    public MyMessage obtain() {
        return MyMessage.obtain(this);
    }
}

5.测试

1.handler 消息发送和接收的流程

/**
 * 测试handler 消息发送和接收的流程
 */
@Test
public void myHandlerTest() {
    //创建当前线程的looper
    MyLooper.prepareLooper();
    //MyLooper.prepareLooper();
    MyHandler myHandler = new MyHandler() {
        @Override
        public void handleMessage(MyMessage myMessage) {
            super.handleMessage(myMessage);
            System.out.println("handleMessage:" + myMessage.what);
        }
    };

    MyMessage myMessage = new MyMessage();
    myMessage.what = 1;
    myHandler.sendMessage(myMessage);

    MyMessage myMessage1 = myHandler.obtain();//从消息池中获取
    myMessage1.what = 2;
    myHandler.sendMessage(myMessage1);

    myHandler.post(new Runnable() {
        @Override
        public void run() {
            System.out.println("runnable is run--->>>");
        }
    });
    //开启消息循环
    MyLooper.looper();

}

2.消息池

我们修改消息池大小为10后测试。

/**
 * 测试消息池
 */
@Test
public void testMessagePool() {
    MyLooper.prepareLooper();
    //MyLooper.prepareLooper();
    MyHandler myHandler = new MyHandler() {
        @Override
        public void handleMessage(MyMessage myMessage) {
            super.handleMessage(myMessage);
            System.out.println("handleMessage:" + myMessage.what);
        }
    };


    for (int i = 0; i < 20; i++) {
        MyMessage myMessage = new MyMessage();//放入消息池中
        myMessage.unCheckRecycle();
    }

    for (int i = 0; i < 20; i++) {
        myHandler.obtain();//从消息池中获取
    }

}