自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android開發(fā)中Handler同步屏障機制(sync barrier)詳解

移動開發(fā) Android
在創(chuàng)建Message對象時調(diào)用Message的setAsynchronous()方法。在一般情況下,異步消息和同步消息沒有什么區(qū)別,但開啟了同步屏障以后就有區(qū)別了。

Handler同步屏障機制是Android開發(fā)中一個較為高級且復(fù)雜的特性,主要用于控制消息隊列MessageQueue中消息的處理順序。當(dāng)設(shè)置同步屏障時,會阻止所有普通消息(同步消息)的處理,同時允許立即消息(例如帶回調(diào)的消息或Runnable對象)繼續(xù)執(zhí)行。

「消息分類」:

  • 「普通消息(同步消息)」:常見的通過Handler發(fā)送的消息,按照時間戳順序在MessageQueue中排隊。我們平時發(fā)的消息基本都是同步消息,在這里不做討論。
  • 「屏障消息(同步屏障)」:一個特殊的Message對象,沒有target屬性,用于在MessageQueue中插入屏障。
  • 「異步消息」:可以通過特定方式標記的消息,優(yōu)先級高于同步消息,即使存在同步屏障也能被處理。

屏障消息(同步屏障)

同步屏障是通過MessageQueue的postSyncBarrier方法開啟。

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;
    }
}
  • 第一步,獲取屏障的的唯一標示,標示從0開始,自加1。
  • 第二步,從Message消息對象池中獲取一個msg,設(shè)置msg為正在使用狀態(tài),并且重置msg的when和arg1,arg1的值設(shè)置為token值。但是這里并沒有給tareget賦值。所以msag的target是否為空是判斷這個msg是否是屏障消息的標志。
  • 第三步,創(chuàng)建變量pre和p,為下一步做準備。其中p被賦值為mMessages,mMessages指向消息隊列中的第一個元素,所以此時p指向消息隊列中的第一個元素。
  • 第四步,通過對隊列中的第一個Message的when和屏障的when進行比較,決定屏障消息在整個消息隊列中的位置,因為消息隊列中的消息都是按時間排序的。
  • 第五步,prev != null,代表不是消息的頭部,把msg插入到消息隊列中。
  • 第六步,prev == null,代表是消息隊列的頭部,把msg插入消息的頭部。

通常通過Handler發(fā)送消息handler.sendMessage(),最終都會調(diào)用Handler.java中的enqueueMessage()方法。

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

可以看到,enqueueMessage()方法里為msg設(shè)置了target字段。而postSyncBarrier()方法也是從Message消息對象池中獲取一個msg插入到消息隊列中,唯一的不同是沒有設(shè)置target字段,從代碼層面上講,屏障消息就是一個target為空的Message。

「工作原理」:Handler的消息處理是在Looper.loop()方法從消息隊列中獲取消息并交給Handler處理,其中是通過MessageQueue是通過next方法來獲取消息的。

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

        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) {
                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.
                    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;
            }
            if (mQuitting) {
                dispose();
                return null;
            }
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        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 {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

msg.target == null時說明此時的msg是屏障消息,此時會進入到循環(huán),遍歷移動msg的位置,直到移動到的msg是異步message退出循環(huán),也就是說循環(huán)的代碼會過濾掉所有的同步消息,直到取出異步消息為止。

當(dāng)設(shè)置了同步屏障之后,next函數(shù)將會忽略所有的同步消息,返回異步消息。設(shè)置了同步屏障之后,Handler只會處理異步消息。同步屏障為Handler消息機制增加了一種簡單的優(yōu)先級機制,異步消息的優(yōu)先級要高于同步消息。

「移除屏障」:屏障不會自動移除,需要手動調(diào)用MessageQueue.removeSyncBarrier(int token)方法移除。token是postSyncBarrier()方法返回的唯一標識符。

public void removeSyncBarrier(int token) {
    // Remove a sync barrier token from the queue.
    // If the queue is no longer stalled by a barrier then wake it.
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        // 循環(huán)遍歷,直到遇到屏障消息時推退出循環(huán)
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization " + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        if (prev != null) {
            // 刪除屏障消息p
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}

刪除屏障消息的方法很簡單,就是不斷遍歷消息隊列,直到找到屏障消息,退出循環(huán)的條件有兩個p.target == null(說明是屏障消息)和p.arg1 == token(說明p是屏障消息,在屏障消息入隊的時候,設(shè)置過msg.arg1 = token)。找到屏障消息后,把它從消息隊列中刪除并回收。

異步消息

通常我們使用Handler想消息隊列中添加的Message都是同步的,如果我們想要添加一個異步的Message,有以下兩種方式:

  1. Handler的構(gòu)造方法有個async參數(shù),默認的構(gòu)造方法此參數(shù)是false,只要在構(gòu)造handler對象的時候,把該參數(shù)設(shè)置為true。
public Handler(@Nullable Callback callback, boolean async) {
    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 = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
    mIsShared = false;
}

async設(shè)置為true后,對全局的mAsynchronous設(shè)置為true。然后在enqueueMessage()調(diào)用msg.setAsynchronous(true)將message設(shè)置為異步的。

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);
}
  1. 在創(chuàng)建Message對象時調(diào)用Message的setAsynchronous()方法。在一般情況下,異步消息和同步消息沒有什么區(qū)別,但開啟了同步屏障以后就有區(qū)別了。
  • 當(dāng)Looper從MessageQueue中取出消息進行處理時,如果遇到屏障消息,會跳過所有后續(xù)的普通消息,直到找到異步消息或屏障被移除。
  • 異步消息不受同步屏障的影響,可以直接被處理。

應(yīng)用場景

  1. 「確保立即任務(wù)優(yōu)先處理」:在需要優(yōu)先執(zhí)行某些緊急任務(wù)時,可以使用同步屏障暫時阻止其他消息的處理。
  2. 「避免死鎖和資源競爭」:在復(fù)雜的消息交互場景中,使用同步屏障可以防止因消息處理順序不當(dāng)引發(fā)的死鎖或資源競爭。
  3. 「UI繪制優(yōu)化」:在Android應(yīng)用框架中,為了更快地響應(yīng)UI刷新事件,ViewRootImpl在繪制流程中使用了同步屏障機制,確保異步繪制任務(wù)可以優(yōu)先執(zhí)行。

注意事項

  1. 「謹慎使用」:不恰當(dāng)?shù)氖褂猛狡琳峡赡軙?dǎo)致消息處理的延遲或阻塞,影響應(yīng)用性能和響應(yīng)能力。
  2. 「手動移除」:使用完同步屏障后,必須手動移除,否則會造成同步消息無法處理。
責(zé)任編輯:武曉燕 來源: 沐雨花飛蝶
相關(guān)推薦

2023-06-26 08:28:35

Sync.CondGolang

2023-06-05 09:23:00

Golang同步工具

2023-06-06 08:28:58

Sync.OnceGolang

2014-06-18 14:41:26

AndroidHandler總結(jié)

2021-11-24 08:33:09

Android廣播機制應(yīng)用程序

2024-04-18 08:27:05

Android數(shù)據(jù)類型

2012-05-25 09:09:25

Windows Pho

2009-03-24 08:56:23

數(shù)據(jù)同步多線程Java

2019-07-25 13:13:25

AndroidHandler消費機制

2011-09-27 10:23:24

Java反射機制

2023-05-11 08:00:44

Golangsync.Pool

2015-01-14 13:50:58

AndroidHandler內(nèi)存泄露

2023-12-25 09:58:25

sync包Go編程

2010-07-07 18:34:43

UML公共機制

2014-05-22 15:41:59

Android消息處理機制Looper

2014-05-22 15:38:27

Android消息處理機制Looper

2014-05-22 15:00:16

Android消息處理機制Looper

2014-05-22 15:04:00

Android消息處理機制Looper

2014-05-22 15:07:44

Android消息處理機制Looper

2014-05-22 15:48:50

Android消息處理機制Looper
點贊
收藏

51CTO技術(shù)棧公眾號