Android消息循环机制解析

从Handler说起

常规的Handler使用过程如下所示,运行如下代码应该就能弹出Toast了

public class MainActivity extends AppCompatActivity {

    private static MyHandler handler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(){
            @Override
            public void run() {
                super.run();
                //耗时任务 ...
                handler.sendEmptyMessage(0);
            }
        }.start();
    }

    public class MyHandler extends Handler{

        private WeakReference<Activity> mActivity;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch(msg.what){
                case 0:
                    if(mActivity.get() != null){
                        Toast.makeText(mActivity.get(),"收到消息",Toast.LENGTH_LONG).show();
                    }
                    break;
            }
        }

        MyHandler(Activity activity){
            mActivity = new WeakReference<>(activity);
        }
    }
}

那么为什么在子线程通过handler.sendMessgae(),主线程就会去执行handler里面的handleMessage(),先来看看Handler的创建过程吧

//Handler.java
public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
     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如果不存在则抛出RuntimeException,由于上文中的Handler是在主线程中进行创建的而在ActivityThread会调用Looper.prepareMainLooper()创建主线程的Looper因此主线程的Looper != null (主线程创建Looper的过程这里有讲),然后来看看Looper.myLooper()

//Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

sThreadLocal是静态变量用来保存每个线程的Looper对象的,在每个线程调用sThreadLocal.get()都获取的是当前线程Looper,既然有get那么是什么时候set进去的呢?来看看Looper.prepareMainLooper()

//Looper.java
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
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对象并将其设置到sThreadLocal里面,下面来看Looper的构造方法

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

里面又创建了MessageQuene实例,下面来看看子线程调用handler.sendEmptyMessage(0)系统都做了哪些东西

//Handler.java
public final boolean sendEmptyMessage(int what) {
    return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();//1
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
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) { //2
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; //3
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
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");
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when; //4
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) { //5
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else { //6
            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;
}

至此消息就已经保存到了Handler所对应的Looper的MessageQuene里面,Message以单链表形式存储,其中MessageQuene里面的成员变量mMessage保存的是单链表的链首,上述代码有几个需要注意
1. Message池中获取一个message,自己用的时候也不要new,也使用Message.obtain()
2. 正常情况下基本不可能为空,应该Handler不为空Looper就不为空那么MessageQuene也不为空
3. 设置Message.target = Handler 保存Handler引用到时候取出消息的时候要用到
4. 设置Message执行的时间
5. 如果单链表为空则直接把该Message赋值给MessageQuene里面的mMessage这个链首对象
6. 若不为空则根据Message的时间先后找到合适的位置进行插入

由于主线程在ActivityThread.main()中会调用Looper.loop()进行死循环不断从MessageQuene里面取出消息,来看看Looper.loop()

//Looper.java
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; //1
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) { //2
            Message msg = queue.next(); // 3
            if (msg == null) { //4
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
            try {
                msg.target.dispatchMessage(msg); //5
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
        }
    }

从上可知该方法是一个死循环,唯一能退出该循环的条件是msg == null
1. 获取该线程所对应的Looper中的MessageQuene实例
2. 死循环一直等待消息
3. 调用MessageQuene.next()尝试从消息队列中取出消息
4. 如果msg == null则退出循环一般只有当外界调用Looper.quit()后才会返回null
5. 调用Handler.dispatchMessage()
先来看看第3点

//MessageQuene.java
Message next() {
        for (;;) {
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        return msg;
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
                if (mQuitting) { //1
                    dispose();
                    return null;
                }
            }
        }
    }

该方法主要内容就是根据mMessager这个单链表链首取出当前时刻已经ready的第一个Message,从注释1处可以得知只有当调用了MessageQuene.quit该方法才会返回null,而Looper.quit()内部调用了MessageQuene.quit(),当Looper取出消息调用Message.target.dispatchMessage(msg),而上文已经讲了当Message插入当单链表中会把Handler设置成Message.target,因此来看看Handler.dispatchMessage(msg)

//Handler.java
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) { //1
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {//2
                    return;
                }
            }
            handleMessage(msg);//3
        }
    }

该方法很简单,就是分派了下消息
1. 如果Message的Callback不为空则直接执行该Message的Callback
2. 如果Handler的mCallback不为空(该成员变量在构造方法中赋值)则执行该Callback并根据返回值判断是否执行Handler.handlerMessage()
3. 当Message.callback为空并且Handler的callback也为空或者不为空但是返回为false,才会执行Handler.handleMessage()

总结

根据源码可以看出其实Handler的作用就是把想要执行的操作从当前线程切换到Handler中Looper所在的线程进行执行。

猜你喜欢

转载自blog.csdn.net/qq_22194581/article/details/79799093