Android handler机制流程详解

六眼飞鱼酱① 提交于 2020-03-04 06:39:53

Android handler机制流程详解

handler机制由以下几部分组成:

.Handler
.Message
.MessageQueue
.Looper

总体流程介绍:

一、进程启动时
在main方法中为主线程创建了对应自己线程的Looper,在Looper被创建的同时会创建一个MessageQueue消息队列并持有。同时在main方法中调用了Looper.loop()进行一个死循环遍历Looper自己持有的消息队列。

二、创建handler
如果在主线程中创建,handler能获取到主线程对应的looper并持有。如果是在子线程中创建handler,则需要自己调用创建looper方法和自己调用Looper.loop。否则会报错。

三、创建Message并发送
创建的Message将创建自己的handler信息封装进Message内部,最终被发送到了MessageQueue消息队列中。

四、接收消息
在Looper.loop中,从MessageQueue中拿出Message,并根据其中对应的handler信息进行分发处理

对应关系:
一条线程Thread对应唯一的一个Looper,其对应相关的信息存储在ThreadLocal中。
一个Looper对象持有一个自己的MessageQueue。
在同一条线程中,无论创建多少handler,都只对应一个Looper和一个MessageQueue。
因此,handler在被创建时就根据Looper与对应的线程进行了绑定,即使在别的线程根据该handler发送了消息,也会被发送到handler对应的Looper的MessageQueue消息队列中并被分发处理。

代码分析:

接下来,举一个简单的例子,并且简单分析一下主流程对应的原码:

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
               
            }
        }
    };
    new Thread() {
            public void run() {
                    Message message = handler.obtainMessage();
                    message.what = 100;
                    message.obj = "测试+ : " + i;
                    handler.sendMessage(message);
            }
        }.start();

解析:
Handler:负责发送消息和接收返回的消息,主流程部分源码分析

public class Handler {
    //主要变量
    final Looper mLooper;//与每个线程对应的looper
    final MessageQueue mQueue;//消息队列
    final Callback mCallback;//是否同步

    //构造方法
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            ..
        }
        mLooper = Looper.myLooper();//根据当前线程信息在ThreadLocal中获取线程对应的looper
        注:
        .如果是在子线程中创建,需要调用Looper.prepare( boolean quitAllowed)
        .该方法的作用是获取调用方法的线程信息,判断当前线程是否有looper,没有则创建。
        .在ActivityThread中创建过主线程的looper

        if (mLooper == null) {
            ..
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }


//从消息对象池中取出消息
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }


//发送消息的各种方法
handler.sendMessage(message);
handler.sendMessageDelayed(
    Message msg, long delayMillis);
handler.sendMessageAtTime(
    Message msg, long uptimeMillis);
handler.sendEmptyMessage(
    int what);
handler.sendEmptyMessageDelayed(
    int what, long delayMillis);
handler.sendMessageAtFrontOfQueue(
    Message msg);
handler.sendEmptyMessageAtTime(
    int what, long uptimeMillis);

    //最终都是调用来发送
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//将当前handler信息封装进Message
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }


//分发消息

    /**
     * 该方法在Looper中next()方法中被调用  根据msg.target来区分
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

}

主要发生的大事:

一、当调用:new Handler() 时:
1、Handler中:Looper.myLooper();
在ThreadLocal中获取当前线程对应的looper,主线程对应的looper在main方法中已经被创建

2、mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
取出looper中的消息队列(mQueue)。Callback对象。是否同步。封装进该handler对象

二、当调用:handler.obtainMessage()时:
在消息对象池中获取消息。

三、当调用:sendMessage或者各种send方法时:
最终调用 enqueueMessage,将当前handler对象封装进message.target,
并调用mQueue.enqueueMessage(msg, uptimeMillis),将消息发送给消息队列

Message:负责封装消息,主流程部分源码分析

public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    long when;
    Bundle data;
    Handler target;
    Runnable callback;
    Message next;
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    private static boolean gCheckRecycle = true;


    //被handler调用
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

    /**
     * 判断消息对象池中是否有消息,并从消息对象池中获取消息
     */
    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();
    }

    省略其他好多方法...

结论:在handler中使用obtain(this)来获取消息池中消息对象时,将当前this封装进message.target中

Looper:不断循环,从消息队列中获取消息,并根据消息的target来进行消息分发,即调用对应handler的dispatchMessage方法。主流程部分源码分析

    public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // 主线程looper对象
    final MessageQueue mQueue; //消息队列
    final Thread mThread;//当前线程

    //构造方法:只有一个构造,并且创建mQueue 和给mThread 赋值
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    //初始化,如果handler在子线程中被调用,则要先调用此方法
    public static void prepare() {
        prepare(true);
    }

    //被上面方法调用
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
            //如果没有创建,则返回空
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建一个looper对象,并将当前线程和该looper绑定
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //主线程在创建的时候在ActivityThread类中main方法中被调用
     创建主线程对应的looper和给当前sMainLooper赋值
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    //handler中获取looper的方法
    public static @Nullable
    Looper myLooper() {
        //该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
        //如果没有创建,则返回空
        return sThreadLocal.get();
    }

    //开启loop循环查询队列,
    主线程对应的loop方法在main中被调用,如果在子线程中创建的handler,需要自己调用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;

        省略众多代码....

        for (; ; ) {
        
        //队列的next()方法中处理基本逻辑,如果有消息:返回。如果没有消息:阻塞||等待
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            省略众多代码.....
            try {
                msg.target.dispatchMessage(msg);//根据targer(即handler)进行分发
                ..
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //将消息放回消息池
            msg.recycleUnchecked();
        }
    }
}

该类负责做的事情:

1、在prepare()被调用时:
该方法可以定义为:创建调用线程对应的looper,判断被调用的线程是否有looper对象,如果有就提示错误,没有则进行创建并绑定线程和looper。

2、构造被调用时:
创建 MessageQueue 消息队列,和给mThread变量(当前线程)赋值

3、在myLooper()被调用时:
该方法可以定义为获取线程对应的looper,判断被调用的线程是否有looper对象,如果有就返回,没有返回空。

4、prepareMainLooper()方法在main方法中被调用,创建主线程对应的looper。

5、loop()方法在main中被调用,作用为死循环从当前线程对应looper持有的消息队列中取出消息,并根据消息对应的targer进行分发。

注:当源码解析到这里有一个疑问:为什么在主线程里面Looper的死循环不会引发ANR
解释:Looper.loop无限循环为什么没有引发ANR

MessageQueue:消息队列,接收handler发送过来的消息存入队列,死循环遍历消息队列返回给looper,消息 队列没有消息时,阻塞||等待。主流程部分源码分析

public final class MessageQueue {

    private final boolean mQuitAllowed;
    是否允许退出
    private boolean mQuitting;//是否放弃消息队列

    @SuppressWarnings("unused")
    private long mPtr;

    private native static long nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层

    private native static void nativeWake(long ptr);//唤醒对应线程


    //构造
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;//是否允许退出
        mPtr = nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层
    }


    //该方法就是在handler中调用send系列方法最终调用的方法,将消息放入消息队列
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//msg.target就是发送此消息的Handler
            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;//将延迟时间封装到msg内部
            Message p = mMessages;//消息队列的第一个元素
            boolean needWake;//是否需要唤醒线程
            if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),
或者此消息不是延时的消息,则此消息需要被立即处理,
此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,
然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理。

                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
//如果此消息是延时的消息,则将其添加到队列中,
原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,
延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,
取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程

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

            if (needWake) {
                nativeWake(mPtr);//唤醒线程
            }
        }
        return true;
    }


    //该方法就是在Looper中被调用的方法,返回一个message,如果队列为空,则阻塞
    Message next() {
        final long ptr = mPtr;
//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,
mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,
阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1;
        int nextPollTimeoutMillis = 0;
        for (; ; ) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞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) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)  
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用
//nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;

                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //正常取出消息
                        //设置mBlocked = false代表目前没有阻塞
                        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 {
                    //没有消息,会一直阻塞,直到被唤醒
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

}

主要做的事:
MessageQueue() 构造:在Looper创建时被创建

enqueueMessage():handler一系列send方法最终殊途同归到这个方法,将消息放入消息队列

next():在Looper.loop() 方法中被调用,遍历队列并返回消息,如果队列中没有消息则阻塞

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!