成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

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

「消息分類」:

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

屏障消息(同步屏障)

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

通常通過Handler發送消息handler.sendMessage(),最終都會調用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設置了target字段。而postSyncBarrier()方法也是從Message消息對象池中獲取一個msg插入到消息隊列中,唯一的不同是沒有設置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是屏障消息,此時會進入到循環,遍歷移動msg的位置,直到移動到的msg是異步message退出循環,也就是說循環的代碼會過濾掉所有的同步消息,直到取出異步消息為止。

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

「移除屏障」:屏障不會自動移除,需要手動調用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;
        // 循環遍歷,直到遇到屏障消息時推退出循環
        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);
        }
    }
}

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

異步消息

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

  1. Handler的構造方法有個async參數,默認的構造方法此參數是false,只要在構造handler對象的時候,把該參數設置為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設置為true后,對全局的mAsynchronous設置為true。然后在enqueueMessage()調用msg.setAsynchronous(true)將message設置為異步的。

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

應用場景

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

注意事項

  1. 「謹慎使用」:不恰當的使用同步屏障可能會導致消息處理的延遲或阻塞,影響應用性能和響應能力。
  2. 「手動移除」:使用完同步屏障后,必須手動移除,否則會造成同步消息無法處理。
責任編輯:武曉燕 來源: 沐雨花飛蝶
相關推薦

2023-06-05 09:23:00

Golang同步工具

2023-06-06 08:28:58

Sync.OnceGolang

2023-06-26 08:28:35

Sync.CondGolang

2014-06-18 14:41:26

AndroidHandler總結

2021-11-24 08:33:09

Android廣播機制應用程序

2024-04-18 08:27:05

Android數據類型

2012-05-25 09:09:25

Windows Pho

2009-03-24 08:56:23

數據同步多線程Java

2011-09-27 10:23:24

Java反射機制

2019-07-25 13:13:25

AndroidHandler消費機制

2023-05-11 08:00:44

Golangsync.Pool

2015-01-14 13:50:58

AndroidHandler內存泄露

2023-12-25 09:58:25

sync包Go編程

2010-07-07 18:34:43

UML公共機制

2012-05-18 11:16:42

@Kroll注解詳解TitaniumAndroid模塊

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:38:27

Android消息處理機制Looper

2014-05-22 15:48:50

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

51CTO技術棧公眾號

主站蜘蛛池模板: 青青草中文字幕 | 精品久久久久久久久久久 | 超碰人人91| 中文字幕视频网 | 精品99在线| 国产高清精品一区 | 天天久久 | 天天爽夜夜骑 | 黑人精品xxx一区一二区 | 国产精品区一区二 | 一区二区三区在线电影 | 亚洲第一av | 日日草天天干 | 99久久99热这里只有精品 | 国产日韩久久 | 成人精品一区二区 | 日韩欧美日韩在线 | 亚洲男人的天堂网站 | 91福利影院 | 黄色一级免费看 | 国产视频精品区 | 中文字幕乱码一区二区三区 | 国产在线麻豆精品入口 | 成人亚洲片 | 久草新在线 | 97精品国产97久久久久久免费 | 三a毛片 | 欧美日产国产成人免费图片 | 国产偷自视频区视频 | 99国内精品 | 91视频免费黄 | 久久久999国产精品 中文字幕在线精品 | www.亚洲视频.com | 久久成人在线视频 | 国产精品极品美女在线观看免费 | 久在线 | 欧美成人一区二区三区 | 成人精品一区二区 | 欧美在线网站 | 久久久久久久久久一区二区 | 久久久久国产一区二区三区 |