1. 程式人生 > >小葵花媽媽課堂開課了:《Handler Looper Message 淺析》

小葵花媽媽課堂開課了:《Handler Looper Message 淺析》

Handler Looper Message Thread

首先要闡述幾者之間的關係。
Thread 可以擁有多個handler物件;
Thread 只能擁有一個Looper 和一個MessageQueue。

Looper 只能屬於一個Thread, 並且只能和MessageQueue 一一對應。 looper的在幾者中的作用是什麼呢!
Looper的作用就是起到 發動機的原理,當然它不是讓車跑起來,而是讓MessageQueue裡的message被執行。
那麼 Message被誰執行呢? 後文即會提到。

MessageQueue 也僅是和一個looper繫結,在出生的時候即決定了這件事,後面在程式碼中會解釋為什麼!
MessageQueue裡面存放就是 Message。

Looper

首先需要關注的是該方法。

public static void prepare() {
    prepare(true);
}

引數為是否允許退出,答案是肯定的 true; 只有一種情況即主執行緒呼叫prepare時傳遞false,因為主執行緒不允許退出。
該方法即為 預熱發動機的入口。讓 Looper這臺機器進行啟動之前的準備工作。

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

分析一下 是如何判斷已經prepare的呢?
sThreadLocal.get() != null
那就需要看一下set是什麼東東。就是準備的是什麼呢?

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on
the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }

這個value就是上文提到的 new Looper(quitAllowed)
createMap建立的是一個ThreadLocalMap。
每一個執行緒僅有一個ThreadLocalMap, 在該map中儲存內容為該執行緒本地變數的副本。ThreadLocalMap使用及注意事項以後單獨開講。

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

當第一次sThreadLocal.get()時,會返回setInitialValue=null;

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

當一個執行緒只能有一個Looper之後也就意味著只能有一個MessageQueue.class

Looper.loop即為啟動發動機的入口,啟動之後開始進行訊息輪詢,並且註釋說明一定要呼叫quit()退出輪詢。
Looper一直把MesageQueue所有的message執行完。
每執行完一個後即通過next拿到下一個message.

public static void loop() {
---
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);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg); //msg.target即為執行message工具。
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

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

MessageQueue
MessageQueue和Looper之間有個緊密的聯絡就是通過 MessageQueue.next()方法。以next方法為切入點介紹MessageQueue.class

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

        //natvie層進行阻塞,後文在Looper.c中介紹
        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) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                // 當因為有 "同步分隔欄" 引起停滯後, 將要找到下一個非同步訊息, 
                // 同步分隔欄後面的同步訊息並不會執行
                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.
                    //如果當前的msg沒有準備好,那麼就下次輪詢進入到等待。
                    //計算等待時間
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    //標記當次輪詢不會被wait,不需要被喚醒
                    mBlocked = false;
                    //當在連結串列佇列中找到可執行msg,把當前message調出,並修復原連結
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    //標記當前msg被使用狀態
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            //如果looper呼叫了quit, messagequeue也進行退出操作。
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            // 引入了另外一個messagequeue的功能, idle handles的處理,
            // 當佇列為空的時候或沒有任務可執行的時候,執行idle handles內容。
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                // 既沒有idle handlers 和message可以處理那麼就需要阻塞,入隊時候就需要喚醒。
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                //執行idler中的回撥,並且有返回值,true意味著想要保持這個idle下次繼續執行,
                //false則會從佇列中移除
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

下面繼續介紹enqueueMessage,入隊操作由Handler.class執行。後文提到其中幾種入隊操作。

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) {
        //如果looper已經條用quit,那麼就放棄入隊。
        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.
            // 如果messagequeue中沒有message或者需要立即執行或者插入message時間優於對頭
            // message所需要執行時間,那麼就把msg插入到對頭。
            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.
            //通常情況下將目標message插入到隊裡中間時,是不需要喚醒佇列的,
            //除非有一個"同步分隔欄"在對頭或者目標message是最早需要執行的非同步message。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    //找到最後一個位置,或者時間排序上晚於目標message的位置
                    break;
                }
                //當需要喚醒,但是 要插入目標message的前面所有位置的message
                //只要有非同步訊息的話既不需要喚醒。
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 將目標message插入到理想位置,修復整個連結
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            //此處為喚醒 Looper
            nativeWake(mPtr);
        }
    }
    return true;
}

上面介紹了MessageQueue的兩個主要方法next()和enqueueMessage(),其中涉及到了兩個native層的本地方法分別為:
nativePollOnce(ptr, nextPollTimeoutMillis);
nativeWake(mPtr);
那麼下面介紹一下這兩個方法。
方法在/frameworks/base/core/jni/android_os_MessageQueue.cpp中進行了定義。

static JNINativeMethod gMessageQueueMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit", "()V", (void*)android_os_MessageQueue_nativeInit },
    { "nativeDestroy", "()V", (void*)android_os_MessageQueue_nativeDestroy },
    { "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },
    { "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
};
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jint ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(timeoutMillis);
}

最終呼叫到Looper::pollOnce====>Looper::pollInner

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
int Looper::pollInner(int timeoutMillis);
int Looper::pollInner(int timeoutMillis) {
    ---
#ifdef LOOPER_USES_EPOLL
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    //通過Epoll進行阻塞
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
#else
    // Wait for wakeAndLock() waiters to run then set mPolling to true.
    mLock.lock();
    while (mWaiters != 0) {
        mResume.wait(mLock);
    }
    mPolling = true;
    mLock.unlock();

    size_t requestedCount = mRequestedFds.size();
    int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
#endif
    ---
}

其中最終運用epoll進行控制(epoll不再本文討論感興趣讀者可自行查詢!
)。下面引入《深入理解Android:卷II》對pollOnce解釋:

其中四個引數:
timeoutMillis引數為超時等待時間。如果值為–1,則表示無限等待,直到有事件發生為止。如果值為0,則無須等待立即返回。
outFd用來儲存發生事件的那個檔案描述符。
outEvents用來儲存在該檔案描述符上發生了哪些事件,目前支援可讀、可寫、錯誤和中斷4個事件。這4個事件其實是從epoll事件轉化而來的。後面我們會介紹大名鼎鼎的epoll。
outData用於儲存上下文資料,這個上下文資料是由使用者在新增監聽控制代碼時傳遞的,它的作用和pthread_create函式最後一個引數param一樣,用來傳遞使用者自定義的資料。
另外,pollOnce函式的返回值也具有特殊的意義,具體如下:
當返回值為ALOOPER_POLL_WAKE時,表示這次返回是由wake函式觸發的,也就是管道寫端的那次寫事件觸發的。
返回值為ALOOPER_POLL_TIMEOUT表示等待超時。
返回值為ALOOPER_POLL_ERROR表示等待過程中發生錯誤。
返回值為ALOOPER_POLL_CALLBACK表示某個被監聽的控制代碼因某種原因被觸發。這時,outFd引數用於儲存發生事件的檔案控制代碼,outEvents用於儲存所發生的事件。

MessageQueue還有其他公開方法:

用來新增IdleHandler,當沒有message需要立即處理時就會處理IdleHandler。

void addIdleHandler(@NonNull IdleHandler handler);
void removeIdleHandler(@NonNull IdleHandler handler);

用來新增需要監聽的檔案描述符fd

void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
            @OnFileDescriptorEventListener.Events int events,
            @NonNull OnFileDescriptorEventListener listener);
void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd);

Message.class
主要是handler要處理的信使,主要功能攜帶引數。下面主要介紹handler引數。

/**
 * User-defined message code so that the recipient can identify 
 * what this message is about. Each {@link Handler} has its own name-space
 * for message codes, so you do not need to worry about yours conflicting
 * with other handlers.
 */
public int what;
//定義在handler中要執行的事件

/**
 * arg1 and arg2 are lower-cost alternatives to using
 * {@link #setData(Bundle) setData()} if you only need to store a
 * few integer values.
 */
public int arg1; 
//如果要儲存簡單的引數,使用arg1和arg2就可以

/**
 * arg1 and arg2 are lower-cost alternatives to using
 * {@link #setData(Bundle) setData()} if you only need to store a
 * few integer values.
 */
public int arg2;

/**
 * An arbitrary object to send to the recipient.  When using
 * {@link Messenger} to send the message across processes this can only
 * be non-null if it contains a Parcelable of a framework class (not one
 * implemented by the application).   For other data transfer use
 * {@link #setData}.
 * 
 * <p>Note that Parcelable objects here are not supported prior to
 * the {@link android.os.Build.VERSION_CODES#FROYO} release.
 */
public Object obj;
//可儲存任意型別引數

/**
 * Optional Messenger where replies to this message can be sent.  The
 * semantics of exactly how this is used are up to the sender and
 * receiver.
 */
public Messenger replyTo;
//可實現跨程序通訊,後面會獨立章節進行講解。

/**
 * Optional field indicating the uid that sent the message.  This is
 * only valid for messages posted by a {@link Messenger}; otherwise,
 * it will be -1.
 */
public int sendingUid = -1;
//與Messenger 配合使用

/*package*/ int flags;
//0x00 非使用, 0x01被使用:當入隊和被回收的時候會設定為1
//0x10 表示為非同步

/*package*/ long when;
//延遲執行時間

/*package*/ Bundle data;
//儲存一些複雜資料

/*package*/ Handler target;
//執行該message的handler

/*package*/ Runnable callback;
//hanlder執行該message時,如果有callback即執行該callback

// sometimes we store linked lists of these things
/*package*/ Message next;
//儲存連結串列

主要解析一下該函式

/**
* Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    //sPoolSync 同步鎖
    synchronized (sPoolSync) {
        //sPool指向連結串列的頭
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            //將sPool取出,並斷鏈
            return m;
        }
    }
    //如果鏈中沒有元素,重新分配
    return new Message();
}

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
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) {
            next = sPool;
            sPool = this;
            sPoolSize++;
            //將該message插入頭部
        }
    }
}

Handler
先比較前幾個Class, Handler比較簡單,成員只有以下幾個:

final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;

先看幾個比較重要的構造方法:

//常用的為無參構造形式
public Handler() {
    this(null, false);
}

//這是無參構造方法呼叫的真正構造方法, 
public Handler(Callback callback, boolean async) {
    //FIND_POTENTIAL_LEAKS
    //將此標誌設定為true以檢測擴充套件的Handler類, 擴充套件的handler類如果不是靜態的匿名,本地或成員類, 
    //則會產生洩漏。我們常見構造時的警告說明!至於消除警告方法一般是設定成靜態或弱引用。
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    //mLooper是來自於sThreadLocal中ThreadLocalMap中 通過呼叫執行緒ID儲存的looper,唯一
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //mqueue來自looper, 也唯一
    mQueue = mLooper.mQueue;
    mCallback = callback;
    //標示該handler傳送的資料是否為非同步資料。
    mAsynchronous = async;
}

通過分析構造方法可驗證前文提到的
handler 僅對應一個looper MessageQueue,,翻過來不成立,也就是說會有多個handler繫結在同一個Looper中。

通過呼叫post(Runnable r); postDelayed(Runnable r, long delayMillis);sendMessage(Message msg);等方法傳送的時間,最終呼叫下面的方法。

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;
    //如果為非同步,則對每一個message進行設定。
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //呼叫enqueueMessage 進行入隊Message
    return queue.enqueueMessage(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;
    }
    //與enqueueMessage差別為uptimeMillis=0. 在messagequeue中當遇到when=0時,
    //會將該message放在對頭進行處理
    return enqueueMessage(queue, msg, 0);
}

在需要注意下,這個remove方法,當傳入null時可將MessageQueue中的所有資料remove掉。

public final void removeCallbacksAndMessages(Object token) {
      mQueue.removeCallbacksAndMessages(this, token);
  }

回過頭來說一下上面的 同步分隔欄,

在Api 23 之, 通過MessageQueue 進行呼叫

/**
 * Posts a synchronization barrier to the Looper's message queue.
 *
 * Message processing occurs as usual until the message queue encounters the
 * synchronization barrier that has been posted.  When the barrier is encountered,
 * later synchronous messages in the queue are stalled (prevented from being executed)
 * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
 * the token that identifies the synchronization barrier.
 *
 * This method is used to immediately postpone execution of all subsequently posted
 * synchronous messages until a condition is met that releases the barrier.
 * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
 * and continue to be processed as usual.
 *
 * This call must be always matched by a call to {@link #removeSyncBarrier} with
 * the same token to ensure that the message queue resumes normal operation.
 * Otherwise the application will probably hang!
 *
 * @return A token that uniquely identifies the barrier.  This token must be
 * passed to {@link #removeSyncBarrier} to release the barrier.
 *
 * @hide
 */
 // 該方法為hide, 正常寫程式碼是呼叫不到的。
public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}

// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
// 同步分隔欄訊息沒有target, 並且arg1用來記錄token
private int mNextBarrierToken;

private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

這個同步分隔message有什麼作用呢?
對開發者沒有明顯的提供,那麼就是在系統及別使用。在ViewRootImpl.java中進行了使用。

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

為了讓View能夠有快速的佈局和繪製,ViewRootImpl在開始measure和layout ViewTree時,會向主執行緒的Handler新增同步分隔message,這樣後續的訊息佇列中的同步的訊息將不會被執行,以免會影響到UI繪製,但是隻有非同步訊息才能被執行。如果想要使用postSyncBarrier() 那麼就需要使用反射進行使用。

總結
Looper、MessageQueue 和 Thread 一一對應。
Handler 需要繫結到一個Looper中, 一個Looper可以有多個Handler。

這是第一文章,以後會多多寫的。歡迎各位指正問題!謝謝。
[email protected]

相關推薦

葵花媽媽課堂開課Handler Looper Message 淺析

Handler Looper Message Thread 首先要闡述幾者之間的關係。 Thread 可以擁有多個handler物件; Thread 只能擁有一個Looper 和一個MessageQueue。 Looper 只能屬於一個Thread, 並

Android深入理解Handler + Looper + Message

宣告:本文是一篇對Handler相關內容的整理(經過相當一段時間,幾次內容增減),有相當部分內容來源網路,其中融入部分作者本身的理解,並加以整理。如有涉及到哪位老師的原作,在此深表感謝! 目錄   Handler + Looper + Message:生產者 + 消費者 + 倉

WebApp 安全風險與防護課堂開課

本文由葡萄城技術團隊於原創並首發 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。   2018 網路安全事故頻發,從資料洩露、資訊竊取,到 DDOS 攻擊、勒索病毒,不僅威脅的總數在增加,威脅態勢也變得更加多樣化,攻擊者在不斷開發新的攻擊途徑

Android非同步處理三Handler+Looper+MessageQueue深入詳解

在《Android非同步處理一:使用Thread+Handler實現非UI執行緒更新UI介面》中,我們講到使用Thread+Handler的方式來實現介面的更新,其實是在非UI執行緒傳送訊息到UI執行緒,通知UI執行緒進行介面更新,這一篇我們將深入學習And

今天做到一道面試題Handler+Looper+MessageQueue+Message的關係

Handler+Looper+MessageQueue+Message的關係 Handler+Looper+MessageQueue這三者的關係其實就是Android的訊息機制。這塊內容相比開發人員都不陌生,在面試中,或者日常開發中都會碰到,今天就來講這三者的關係。 概述

Android非同步處理Handler+Looper+MessageQueue深入詳解

概述:Android使用訊息機制實現執行緒間的通訊,執行緒通過Looper建立自己的訊息循 環,MessageQueue是FIFO的訊息佇列,Looper負責從MessageQueue中取出訊息,並且分發到訊息指定目標Handler對 象。Handler物件繫結到執行緒的區域性變數Looper,封裝了傳送訊

Handler Looper Message的原始碼分析

Handler Handler是用來分發和處理訊息的,通常我們建立Handler都是使用其無引數的構造方法 public Handler() { this(null, false); } 其內部呼叫的是2個引數的構造方法 public Handler(Ca

Handler,Looper,Message,MessageQueue,HandlerThread使用總結(上)

在安卓程式中,經常會有一些耗時的操作例如下載,網路訪問等,如果將這些放在主執行緒執行,會很耗時,這樣可能會導致一個異常 叫ANR異常(Application Not Responding)將會阻塞UI執行緒,從而會導致程式無響應。因此我們會將一些耗時操作放在子執行緒進行,

編帶你簡單解一下加密技術原理AES加密標準

數據 aes加密 體制 結構 方向 插值 空間 基於 領域 隨著因特網的發展,信息傳輸及存儲的安全問題成為影響因特網應用發展的重要因素。信息安全技術也就成為了人們研究因特網應用的新熱點。 信息安全的研究包括密碼理論與技術、安全協議與技術、安全體系結構理論、信息對抗理論與技術

i機器人又雙叒叕接工作醫療智慧問詢機器人“貝貝”上崗

2018年10月31日,名叫“貝貝”的實體互動智慧問訊機器人在復旦大學附屬兒科醫院門診大廳上崗,現場解答患者疑問。不僅大大方便了廣大患兒及家長,而且著實減輕了門診問詢視窗工作人員的工作強度。 醫院向來人滿為患,特別是在兒童醫院,有寶寶生病的家長都是恨不得第一時間見到大夫。“貝貝”的及時響應在

真的炸讓人頭痛的程式之『圖片懶載入』終極解決方案

轉載:請寫明原文連結及作者名 ‘小小小’ 小程式真的會取代一切?QQ群:139128168 ← 點選加群 微信小程式中,懶載入特效讓人頭疼不已,因為小程式完全沒法操作dom,所以位置的操作在小程式中,變得極其的難~~ 先看特效: 我們將其拆分為如

WebApp 安全風險與防護課堂(第二講)開課

本文由葡萄城技術團隊於原創並首發 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。   在昨天的公開課中,由於參與的小夥伴們積極性和熱情非常高,我們的講師Carl(陳慶)把原定第二講的大部分也一併獻出了,所以原定三場的公開課也變為了兩場,本系列

再不努力提高效率,姐姐都被人追走K8S一鍵部署瞭解一下?

隨著網際網路時代的不斷髮展,開發者可能會面臨這樣的困境:為了解決問題、提升開發效率而竭力研發出來的“創新”,似乎削弱了他們在公司的重要程度,甚至取代了他們原先的地位。比如,在雲原生時代,部分企業更願意選擇 K8s 來解決運維、彈性的問題,而不是組建一支需要耗費大量僱傭資金、管理資金的研發團隊。 對於 K8s,

讀車神探來上海車展大爆移動視頻商業化圖謀

上海車展 長安汽車 人民網 業內人士 國際車展 作為重磅國際車展之一的上海車展已經於上月28日正式閉幕,據不完全統計,展會期間共接待觀眾近26萬人,現場購車及訂車16000余輛,銷售金額近24.3億元人民幣。文/張書樂TMT行業觀察者、遊戲產業時評人,人民網、人民郵電報專欄作者但較以往車展

數據說話6萬一房成“全球第二貴“ 狂跌的深圳房價5月又回暖?

國家 swd src alt 數據庫 還在 繼續 webp 數據管理 深圳是一個房價不低的城市,這點沒人敢說否。的確,深圳經過20年的樓市發展,從05年的6000元,到2008年的1萬,到2005年的3.5萬,到2017年的5萬,這就是房價的歷程。 但是3月樓市

Android實戰技巧之三十八Handler使用中可能引發的內存泄漏

sha 指向 ons har 引用 destroy 對象 from weak 問題描寫敘述 曾幾何時,我們用原來的辦法使用Handler時會有以下一段溫馨的提示: This Handler class should be static or le

微信程序自學第三課文件作用域,模塊化

數據 變量 span data ava 有效 函數 方法 oba 一、文件作用域   在 JavaScript 文件中聲明的變量和函數只在該文件中有效;不同的文件中可以聲明相同名字的變量和函數,不會互相影響。 通過全局函數 getApp() 可以獲取全局的應用實例,如果需要

強烈推薦 在線接口文檔管理工具 幺雞 團隊可以省掉測試

des 管理工具 margin 之前 接口 示例 註意事項 order 阿裏雲 在朋友那兒看到一個不錯的在線文檔管理工具 主要特點 : 在線接口測試在線測試,方便前後端開發,降低錯誤率。支持:xml、json、txt、binary、websocket可視化編輯與分享

<田吃餃子> LINUXContos7.0 / 7.2 LAMP+R 下載安裝Php篇

ssl itl 搭建 hle file 使用 mcr not soc 更新時間:2017-09-21 16:03 簡介 LAMP+R指Linux+Apache+Mysql+PHP+Redis是一組常用來搭建動態網站或者服務器的開源軟件,本身都是各自獨立的程序,但是因為常

<田吃餃子> LINUXContos7.0 / 7.2 LAMP+R 下載安裝Redis篇

php+redis pac apache ron 在一起 tor blank amp .cn 更新時間:2017-09-21 15:38 簡介 LAMP+R指Linux+Apache+Mysql+PHP+Redis是一組常用來搭建動態網站或者服務器的開源軟件,本身都是各自獨