示例
Looper 的使用示例:
public class LooperThread extends Thread{ private Handler mHandler; @Override public void run() { Looper.prepare(); mHandler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { //process incoming messages here } }; Looper.loop(); } }
Looper
用于在指定线程中运行一个消息循环,一旦有新任务则执行,执行完继续等待下一个任务,即变成 Looper 线程。
创建
Looper 的创建需要通过Looper.prepare()
。
在构造函数中创建了一个消息队列MessageQueue
的实例mQueue
,并持有当前线程对象的引用mThread
。
@UnsupportedAppUsage static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); ... /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
ThreadLocal
ThreadLocal 并不是线程,它的作用是可以在每个线程中存储数据。可以在不同的线程中互不干扰地存储并提供数据,通过它可以轻松获得每个线程的 Looper。
运行
使用Looper.loop()
在当前线程运行消息队列。(在这之前需要调用 prepare 方法,否则会抛出异常)
在for
循环中,不断调用MessageQueue
对象queue
的 next 方法来获取下一个待处理的消息Message
对象message
。msg.target.dispatchMessage(msg); msg.target
是 handler 对象。
注:next 方法可能会发生阻塞。
Looper.loop 方法源码:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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."); } if (me.mInLoop) { Slog.w(TAG, "Loop again would have the queued messages be executed" + " before this one completed."); } me.mInLoop = true; 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(); // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); boolean slowDeliveryDetected = false; 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); } // Make sure the observer won't change while processing a transaction. final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; Object token = null; if (observer != null) { token = observer.messageDispatchStarting(); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } 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(); } }
Message
Message
这个类定义了一个包含描述和一个任意类型对象的对象,它可以被发送给 Handler。
/** * * Defines a message containing a description and arbitrary data object that can be * sent to a {@link Handler}. This object contains two extra int fields and an * extra object field that allow you to not do allocations in many cases. * * While the constructor of Message is public, the best way to get * one of these is to call {@link #obtain Message.obtain()} or one of the * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull * them from a pool of recycled objects. */
从注释里我们还可以了解到以下几点:
- 尽管 Message 有 public 的默认构造方法,但是你应该通过 Message.obtain()来从消息池中获得空消息对象,以节省资源。
- 如果你的 message 只需要携带简单的 int 信息,请优先使用 Message.arg1 和 Message.arg2 来传递信息,这比用 Bundle 更省内存
- 用 message.what 来标识信息,以便用不同方式处理 message。
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ 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(); }
MessageQueue
在 Looper 的构造函数中创建了 MessageQueue,在 loop 过程中,通过死循环不断的获取消息。 可以通过Looper.myQueue()
获取。
MessageQueue 类注释如下:
/** * Low-level class holding the list of messages to be dispatched by a * {@link Looper}. Messages are not added directly to a MessageQueue, * but rather through {@link Handler} objects associated with the Looper. * * <p>You can retrieve the MessageQueue for the current thread with * {@link Looper#myQueue() Looper.myQueue()}. */
Handler 中调用了 MessageQueue 对象的 enqueueMessage 函数,将 Message 对象发送到队列中。
Handler 中:
public final boolean sendMessageAtFrontOfQueue(@NonNull 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); } private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
MessageQueue 中:
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } 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) {//队列头 msg.next = p; mMessages = msg; needWake = mBlocked; } else { 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; }
Handler
Handler
的实际应用就是 UI 线程与其他线程之间的切换。
创建
Handler 有多个构造函数,主要是设置三个参数 Looper 对象,Callback 对象,布尔类型 async。Callback 对象主要涉及消息的处理,async 表示是否设置同步屏障。
public Handler(@NonNull Looper looper) public Handler(@NonNull Looper looper, @Nullable Callback callback) public Handler(boolean async) public Handler(@Nullable Callback callback, boolean async) public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)
消息传递
通过 sendMessage 发送一个 Message 对象,或通过 post 方法提交一个 Runable 对象。 而 post 函数只是将 Runable 对象封装到 Message 对象中的 callback 函数。最终还是调用 sendMessagedDelayed 函数的历程去处理。 从这里我们定位到了在 Looper 的 loop 函数中 Message 对象的 target 是 Handler 对象,看看 dispatchMessage 函数。
public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
从 dispatchMessage 函数可以看出对消息处理的优先级。
消息 Message 对象 msg 自带的 Runable 对象 callback。也就是我们通过 post 函数投递的 Runable 对象会最先被处理。 优先级排第二就是我们在创建 Handler 对象时,设置的全局回调 Callback 对象 mCallback。 优先级最低的,也是最常用的,重载 handleMessage 函数,该函数默认是空实现。
sendMessage 与 obtainMessage
public final boolean sendMessage(@NonNull Message msg); //传入一个 Message 参数,进行排队发送到 handleMessage public final Message obtainMessage(); //返回值是一个 Message,一般搭配 sendToTarget 使用 有多个重载版本,就是构建传入参数的不同产出不同的 Message public final Message obtainMessage(int what); //带指定 what 的 Message public final Message obtainMessage(int what, @Nullable Object obj);//带指定 what 和 obj 的 Message public final Message obtainMessage(int what, int arg1, int arg2);//带指定 what arg1 arg2 的 Message public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj);带指定 what arg1 arg2 obj 的 Message 搭配使用就是 obtainMessage(xx).sendToTarget(); //实现和 sendMessage 相同的功能
obtainMessage 会利用内部的 message 池,如果池中有可用 message,就不重新 new 分配,参考 Message 的构造函数。