博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Handler原理
阅读量:4180 次
发布时间:2019-05-26

本文共 14119 字,大约阅读时间需要 47 分钟。

前言:

        对于handler的运行,其实已经看过好多遍了,现在也算有个清晰的理解了,handler我们实际用到的地方其实就是更新UI,handler运行主要涉及到四个类Handler,Looper,MessageQueue和Message。

        先来简单说下这几个类的作用:

        1、Message:可以理解为存放任务的载体,是一个单链表的数据结构;

        2、MessageQueue:可以理解为是一个容器,这个容器就是存放任务的(Message),这个容器有一个功能就是会根据任务执行的时间进行排序;

        3、Looper:这个可以理解为是一个发动机,其实就是一个死循环,不停地去遍历MessageQueue查询里面的任务,遍历MessageQueue的时候如果没有任务了,则会处于一个阻塞状态,这个阻塞不会消耗cpu;

        4、Handler:这个就是发任务和处理任务了,处理任务时会将任务分发给目标处理者。

Message:

        首先我们来看一下这个消息体的几个消息变量:

 

public int what;                    //一个消息体的标示public int arg1;                    public int arg2;public Object obj;                  //消息的内容,agr1,agr2,obj/*package*/ long when;              //什么时候开始执行这个任务/*package*/ Bundle data;            //消息内容/*package*/ Handler target;         //这个消息是由哪个Handler发送和执行的/*package*/ Runnable callback;      //这个消息的任务,如果为null,就会执行handler中的/*package*/ Message next;           //标记这个消息的下一个消息是哪一个

可以看出就是封装了一个任务和这个任务执行时所需要的数据。说白了就是这个消息体知道它的任务是由谁来执行的(Handler),然后把它需要的数据给他就ok,至于是怎么做的他就不管了。

        我们来看一下这个消息体需要注意些什么,首先看下他是怎么回收的:

 

void recycleUnchecked() {    // Mark the message as in use while it remains in the recycled object pool.    // Clear out all other details.    flags = FLAG_IN_USE;    //标示这个消息在使用    what = 0;    arg1 = 0;    arg2 = 0;    obj = null;    replyTo = null;    sendingUid = -1;    when = 0;    target = null;    callback = null;    data = null;    synchronized (sPoolSync) {        if (sPoolSize < MAX_POOL_SIZE) {            //将这个消息体复位后,再将之前用完的Message接到这个消息体后面,以便后面使用            //说白了就是维护了一个消息池            next = sPool;              sPool = this;            sPoolSize++;        }    }}

这里的意思就是用完的消息体维护在一个消息池中,如果这个消息池的大小小于50,就保存起来,方便下次使用,如果大于五十那就不管了,也就意味着等着gc回收掉了。

        对于消息体的创建,一是可以直接new来创建,不过不建议,后面会讲到;其次就是它的obtain()方法:

 

public static Message obtain() {    synchronized (sPoolSync) {        if (sPool != null) {            Message m = sPool;            sPool = m.next;            m.next = null;            m.flags = 0; // clear in-use flag            sPoolSize--;            return m;        }    }    return new Message();}

可以看到这个方法就是从消息池中去拿,并清除flags(说明这个消息体没在使用中),这样一来就免去了频繁的去创建消息体了,Android的运行就是基于消息驱动的,像Activity的生命周期方法就是就是通过消息来执行的。所以下次使用消息的时候就不要再去new了,正确的获取方法应该是Messge的obtain() 方法。

MessageQueue:

        顾名思义,这个就是装消息体的容器了,来看一下它是怎么添加消息的:

 

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) {        //判断这个消息队列是否已经放弃,默认是false        //当调用了quit(boolean safe)时为true        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;}

这个添加消息的方法总结一句:根据时间对消息进行排序,越早执行的就在前面。

        看完添加消息后当然是看看怎么取出消息的了,这里只是列出取出消息的逻辑:

 

Message next() {    // Return here if the message loop has already quit and been disposed.    // This can happen if the application tries to restart a looper after quit    // which is not supported.    final long ptr = mPtr;    if (ptr == 0) {        return null;    }    int pendingIdleHandlerCount = -1; // -1 only during first iteration    int nextPollTimeoutMillis = 0;    for (;;) {        if (nextPollTimeoutMillis != 0) {            Binder.flushPendingCommands();        }        //阻塞操作,当有新消息加入并需要,消息队列被唤醒,会返回        //nextPollTimeoutMillis这个是下个消息执行所需要等待的时间,时间到了会返回        nativePollOnce(ptr, nextPollTimeoutMillis);        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) {                // 当消息的handler为null时,查询下一条消息并退出                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 {                    // 这里就是拿到队首的消息并返回                    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;            }            // Process the quit message now that all pending messages have been handled.            if (mQuitting) {                //消息退出,返回为null                dispose();                return null;            }        }    }}

这里就是拿到队首的消息,如果队首的消息还没到执行时间,就阻塞直到这个消息执行或是有新消息需要执行时也会唤醒。

        再来看一下它是怎么移除消息的:

 

void removeMessages(Handler h, Runnable r, Object object) {    if (h == null || r == null) {        return;    }    synchronized (this) {        Message p = mMessages;        // 从消息头部开始移除所有符合条件的消息对象        while (p != null && p.target == h && p.callback == r                && (object == null || p.obj == object)) {            Message n = p.next;            mMessages = n;            p.recycleUnchecked();            p = n;        }        // 移除剩余的所有符合条件的消息对象        while (p != null) {            Message n = p.next;            if (n != null) {                if (n.target == h && n.callback == r                        && (object == null || n.obj == object)) {                    Message nn = n.next;                    n.recycleUnchecked();                    p.next = nn;                    continue;                }            }            p = n;        }    }}

        这里主要用到两个循环,第一个循环是从头部开始,移除所有符合条件的连续的消息,第二个循环移除剩下的所有符合条件的消息对象。

Looper:

        消息队列有了,接下来就来看看Looper是如何去取消息了,首先来看看Looper是如何启动的,一般Looper的启动要执行两个方法,Looper.prepare();Looper.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));}

这个方法就是创建一个looper对象,在然后当前线程去保存looper,关于ThreadLocal不懂的可以参考。看看looper的构造方法:

 

private Looper(boolean quitAllowed) {    mQueue = new MessageQueue(quitAllowed);    mThread = Thread.currentThread();}

这不就在looper中创建了一个消息队列么,这下消息队列就和looper有了关系了。

        再来看看looper()方法,只保留了部分逻辑代码:

 

public static void loop() {    //获取当前线程的looper对象    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 (;;) {        //这里就是从消息队列中去取消息,可能会阻塞,MessageQueue中有分析        //当消息队列取消是,这里会返回null,退出循环        Message msg = queue.next(); // might block        if (msg == null) {            // No message indicates that the message queue is quitting.            return;        }        //默认为null,可通过setMessageLogging()方法来指定输出        final Printer logging = me.mLogging;        if (logging != null) {            logging.println(">>>>> Dispatching to " + msg.target + " " +                    msg.callback + ": " + msg.what);        }                final long end;        try {            //这里就是将消息分发到handler的dispatchMessage(msg)去处理            msg.target.dispatchMessage(msg);            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();        } finally {            if (traceTag != 0) {                Trace.traceEnd(traceTag);            }        }                //这里就是回收消息,将消息放入消息池中,可以看Message的分析        msg.recycleUnchecked();    }}

这里就是拿到消息队列,然后从消息队列中去取消息并做处理,最后在对消息进行回收。

Handler:

        最后的最后,就剩下Handler了,这个发任务和处理任务的对象,也是我们使用的最多的对象,通过对Looper的分析,我们知道消息最终是分发到handler的dispatchMessage()中来处理了,我们来瞧瞧这个方法:

 

public void dispatchMessage(Message msg) {    //对消息由谁来处理进行判断    //优先级最高的是msg.callback,是一个实现了Runnable接口的对象    //其次是handler中的Callback,    //最后才轮到handler中的handleMessage(msg)方法    if (msg.callback != null) {        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);    }}private static void handleCallback(Message message) {    message.callback.run();}

这里就是对任务进行分发了,看看到底由谁来处理,优先级高到底:Message的callback,Handler的mCallback,Handler的handleMessage()方法。

        接下来看看Handler的构造方法,Handler的构造方法有好几个,但最后调用的都是

 

public Handler(Looper looper, Callback callback, boolean async) {    mLooper = looper;           //处理消息的looper,如果不设置,默认是主线程的looper    mQueue = looper.mQueue;     //拿到了消息队列,添加消息就靠它了    mCallback = callback;       //设置回调,如果不为null,handleMessage(msg)这个方法就不会调用了    mAsynchronous = async;      //设置消息是否为异步处理}

        Handler有了,现在就剩发现消息了,发消息主要有两种方式:一种是以post开头的,一种是以send开头的,

 

public final boolean post(Runnable r) {    return  sendMessageDelayed(getPostMessage(r), 0);}public final boolean postAtTime(Runnable r, long uptimeMillis) {    return sendMessageAtTime(getPostMessage(r), uptimeMillis);}public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);}public final boolean postDelayed(Runnable r, long delayMillis) {    return sendMessageDelayed(getPostMessage(r), delayMillis);}public final boolean postAtFrontOfQueue(Runnable r) {    return sendMessageAtFrontOfQueue(getPostMessage(r));}private static Message getPostMessage(Runnable r) {    Message m = Message.obtain();    m.callback = r;    return m;}private static Message getPostMessage(Runnable r, Object token) {    Message m = Message.obtain();    m.obj = token;    m.callback = r;    return m;}

post里就是获取一个消息对象并赋值,可以看到post发的消息都有一个Runnable参数,这个参数最终赋值给的就是Message中的callback,post调用的还是send,那就来看看send是如何发消息的:

 

public final boolean sendMessage(Message msg) {    return sendMessageDelayed(msg, 0);}public final boolean sendEmptyMessage(int what) {    return sendEmptyMessageDelayed(what, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {    Message msg = Message.obtain();    msg.what = what;    return sendMessageDelayed(msg, delayMillis);}public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {    Message msg = Message.obtain();    msg.what = what;    return sendMessageAtTime(msg, uptimeMillis);}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);}//这个是将消息放到消息队列的最前面public final boolean sendMessageAtFrontOfQueue(Message msg) {    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, 0);}

也是获取消息然后赋值,这个与post唯一的不同就是处理任务的回调不一样,post处理任务的是Message的callback,而send则是handler的callback或是handleMessage(),上面dispatchMessage()有分析到。从上面可以看到,所有post,send发的消息最后都是通过enqueueMessage()添加的,那就来看看这个方法了:

 

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

就是调用了MessageQueue的enqueueMessage()。对于Handler移除消息,我们来看下它的方法:

 

public final void removeCallbacks(Runnable r) {    mQueue.removeMessages(this, r, null);}public final void removeCallbacks(Runnable r, Object token) {    mQueue.removeMessages(this, r, token);}public final void removeCallbacksAndMessages(Object token) {    mQueue.removeCallbacksAndMessages(this, token);}

其实调用的就是MessageQueue中移除消息的方法,上面MessageQueue中有分析到。

总结:

          1、Handler通过enqueueMessage()将消息添加到MessageQueue中;

          2、Looper通过不断循环遍历MessageQueue拿到Message,并将message交给target(Handler)处理;

          3、Handler的dispatchMessage()通过优先级分发相应的callback处理,优先级顺序(高到底):

                3.1 、Message的回调:message.callback.run();

                3.2、Handler的回调:Handler.mCallback.handleMessage();

                3.3、Handler的handlerMessage()方法。

           4、MessageQueue中没有消息或是没有立即执行的消息,那么MessageQueue会处于阻塞状态。

 

转载地址:http://czhai.baihongyu.com/

你可能感兴趣的文章
230. Kth Smallest Element in a BST(Tree)
查看>>
求字符串的最长回文串-----Manacher's Algorithm 马拉车算法
查看>>
回溯法常用的解题模板和常见题型
查看>>
深入分析Java I/O 的工作机制
查看>>
动态规划的套路----左神
查看>>
KMP算法简解
查看>>
左神算法课进阶版总结
查看>>
左神算法基础班总结
查看>>
Linux性能优化
查看>>
进程间的通信---UNIX高级环境编程
查看>>
基于SSH开发的城市公交管理系统 JAVA MySQL
查看>>
基于SSH开发的勤工助学管理系统 JAVA MySQL
查看>>
基于SSH开发的宠物销售商城系统 JAVA MySQL
查看>>
基于springboot的宠物领养管理系统 java
查看>>
JAVA 洗衣房管理系统 宿舍洗衣机管理系统
查看>>
基于SSM的街道办安全管理系统 JAVA
查看>>
基于SSM的论文选题管理系统 JAVA
查看>>
生成器模式
查看>>
工厂方法模式
查看>>
阿里规范(一)关于CountDownLatch和ThreadLocalRandom的详解(带测试代码)
查看>>