彻底搞明白Android消息机制

429 阅读8分钟

Android中的消息机制主要是指Handler的运行机制,而Handler的运行又离不开Looper和MessageQueue。

MessageQueue

  • MessageQueue用来接收Hnadler发送过来的消息Message,其内部是用单链表的数据结构来存储消息列表的。
//MessageQueue.class
Message next() {
    ...
    for (;;) {
        ...
        synchronized (this) {
            ...
            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 {
                    // Got a message.
                    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;
            }
            ...
        }
        ...
    }
}
  • MessageQueue在调用Looper的prepare方法时创建。
//Looper.class
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");
    }
    //将Looper存到当前线程的ThreadLocal中
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    //创建MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
  • MessageQueue的next方法是一个无限循环,除非调用quit方法才会停止
Message next() {
    ...
    for (;;) {
        ...
        synchronized (this) {
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                //返回null,退出循环
                return null;
            }
        }
        ...
    }
    ...
}

void quit(boolean safe) {
    ...
    synchronized (this) {
        ...
        //这样MessageQueue就退出循环啦
        mQuitting = true;
        ...
    }
}

Looper

  • Looper用于从MessageQueue中取出待处理的消息,会调用MessageQueue的next方法来取消息。如果MessageQueue中没有消息,Looper就会一直阻塞等待,除非MessageQueue返回null才退出消息的循环监听。
  • 调用Looper的prepare方法后会创建MessageQueue和Looper。但需要调用Looper的loop方法才会开启消息循环,Looper才会开始循环监听MessageQueue中的消息
  • Looper取到消息后,会调用消息Message所对应的Handler的dispatchMessage方法来处理消息。这样Handler的dispatchMessage方法就会在Looper所在的线程中执行了。
public static void loop() {
    final Looper me = myLooper();
    //调用loop方法前,必须先通过prepare方法创建looper和消息队列MessageQueue
    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 (;;) {
        //调用MessageQueue的next方法取消息,因为next是个无限循环,所以loop方法也会阻塞
        Message msg = queue.next(); // might block
        //只有在调用MessageQueue的quit方法后MessageQueue才会返回null,才会退出循环监听
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        ...
        
        try {
            //msg.target就是Handler,这里就是调用Handler的dispatchMessage方法来处理消息。
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        ...

    }
}

Handler

  • Looper获取到消息后,最后会交给Handler的dispatchMessage方法处理
//Handler.class
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
  • 首先看看Message的callbac是否为null,不为null就通过handleCallback方法来处理消息。
private static void handleCallback(Message message) {
    message.callback.run();
}
  • Message的callback是个Runnable对象,是Handler的post方法传递的Runnable参数。
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
  • 如果callback参数为null,就检查mCallback参数是否为null。不为null就调用mCallback的handleMessage方法处理消息。mCallback是一个Callback接口
/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}
  • 如果我们在使用Handler时不想派生Handler的子类,就可以用Callback的方式
private Handler callbackHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {

        return false;
    }
});
  • 如果mCallback参数也为null,那就调用handleMessage方法处理消息,也就是我们用派生Handler子类的方式创建Handler时重写的handleMessage方法
//Handler.class
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}
  • 用派生Handler子类的方式创建Handler
private static class MyHandler extends Handler {
    @Override
    public void handleMessage(Message message) {
        switch (message.what) {
            case MESSAGE_TAG:
                Log.d(Thread.currentThread().getName(),"receive message");
                break;
            default:

                break;
        }
    }
}

Handler为什么能切换线程?

一句话总结就是:Handler是线程间共享的一个对象,而Looper则是每个线程私有的。

怎么理解上面的说法呢?
  • Handler是共享的意思是,在需要切换的2个线程中都能访问我们创建的Handler对象,比如能调用Handler的sendMessage方法。
  • 比如下面的代码中myHandler变量是在HandlerActivity的onCreate方法中创建的,也就是在UI线程中。myHandler即可以在UI线程中调用,也可以在子线程中调用。
public class HandlerActivity extends Activity {

    public static final int MESSAGE_TAG = 0x01;
    private MyHandler myHandler;

    //通过派生Handler子类的方式创建我们的Handler
    private static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MESSAGE_TAG:
                    Log.d(Thread.currentThread().getName(),"receive message");
                    break;
                default:

                    break;
            }
        }
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //创建Handler
        myHandler = new MyHandler();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = MESSAGE_TAG;
                //在子线程中调用UI线程创建的myHandler,发送消息
                myHandler.sendMessage(message);
            }
        }).start();
    }
}
  • 调用Handler发送消息以后怎么就把线程从子线程切换到UI线程了呢?这是因为每一个Handler在创建的时候都需要当前线程有Looper,而每个线程都会有自己的Looper
//Handler.class
public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    ...
    //获取Looper,如果当前线程没有Looper就会抛异常
    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.class
//每个线程都有一个自己的ThreadLocal,用于存储当前线程中的数据
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
    //每个线程只能有一个Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //第一次调用Looper的prepare方法创建Looper时就会将Looper存入当前线程的ThreadLocal中
    sThreadLocal.set(new Looper(quitAllowed));
}
//从当前线程中的ThreadLocal中获取Looper
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
  • 从上面的源码中我们可以看到,每个Handler都对应一个Looper,而每个线程都有自己的Looper,而且每个线程只能有一个Looper。虽然每个线程我们可以创建多个Handler,但是实际上对应的都是一个Looper。
//Handler.class
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

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;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //往MessageQueue中插入消息
    return queue.enqueueMessage(msg, uptimeMillis);
}
  • 从上面的源码中我们可以看出Handler的sendMessage方法所做的只是将Message插入到对应的MessageQueue当中。这样Looper调用MessageQueue的next方法就会取到添加的信息,Looper取到信息以后就会调用Handler的handleMessage方法处理信息。
  • 而因为上面我们分析过,Looper是每个线程独有的。那这个Looper所在的线程是哪个,上面调用Handler的handleMessage方法的逻辑就是在哪个线程中执行的。
  • 所以Handler的handleMessage方法是运行在创建Handler的线程中的。

总结

Handler机制

我们用上面派生Handler的子类的例子来总结下Handler机制

  • 首先我们在需要执行Handler的handleMessage方法的线程中创建Handler,比如在UI线程中,这样以来后面我们在子线程中执行完耗时的任务后就可以给Handler发送一个消息,最后在Handler的handleMessage方法中更新UI
  • 在子线程中执行完耗时任务,比如网络请求后,将结果封装在Message中通过Handler的sendMessage方法把消息插入到Handler的消息队列
  • Handler的Looper取到MessageQueue中的消息后交由Handler调用handleMessage方法执行。从而就把逻辑从子线程切换到了UI线程,并带回了子线程的执行结果

一句话就是:我们在UI线程中创建的Handler,里面用的Looper是UI线程的。当我们在子线程中调用sendMessage方法插入消息后,Handler的Looper会去取消息出来,然后调用Handler的handleMessage方法。所以handleMessage方法执行的线程就是Looper所在的线程,这样就从调用sendMessage方法的子线程切换到了调用handleMessage方法的Looper所在的线程了。

使用Handler的3种方式
  • 使用Handler有3种方式,分别是:派生Handler子类、Callback接口、post一个Runnable
  • 派生Handler子类、Callback接口上面的分析中都举过例子,post一个Runnable的方式并不常用,这里也贴个简单的例子
public class HandlerActivity extends Activity {
    private Handler myHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myHandler = new Handler();

        new Thread(new Runnable() {
            @Override
            public void run() {
                myHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(Thread.currentThread().getName(),"receive message");
                    }
                });
            }
        }).start();
    }
}
  • 需要注意理解的是上面的run方法执行的线程也是在创建Handler的线程,在这个例子中就是UI线程。其实就是相当于把handleMessage方法中的代码直接放在run方法中了而已。
注意点
  • 主线程,也就是UI线程中的Looper和消息队列,在我们启动应用的时候系统已经为我们创建好了,所以我们可以在Activity中直接就创建Handler而不会报错
  • 消息队列MessageQueue底层是单链表结构,插入删除有优势。通过Handler发送的多条消息会按发送的顺序排队执行。
  • 由于Looper的loop方法是个无限循环,所以子线程中创建的Handler在明确不需要再使用时需要调用Looper的quit方法来退出循环。

今天你进步了嘛?欢迎关注我的微信公众号,和我一起每天进步一点点!

AntDream