欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Loop機(jī)制中Looper與handler詳細(xì)分析

 更新時(shí)間:2022年11月15日 16:05:38   作者:Super-B  
Handler是Android線程之間的消息機(jī)制,主要的作用是將一個(gè)任務(wù)切換到指定的線程中去執(zhí)行,準(zhǔn)確的說(shuō)是切換到構(gòu)成Handler的looper所在的線程中去出處理。本文將詳細(xì)介紹Android Handler機(jī)制和Looper Handler Message關(guān)系

Looper是什么

用于為線程運(yùn)行消息循環(huán)的類。默認(rèn)情況下,線程沒有與之關(guān)聯(lián)的消息循環(huán)。要?jiǎng)?chuàng)建一個(gè),在要運(yùn)行循環(huán)的線程中調(diào)用 prepare(),然后調(diào)用loop()讓它處理消息,直到循環(huán)停止為止。與消息循環(huán)的大多數(shù)交互是通過(guò) Handler類進(jìn)行的。

意思大概就是讓線程有處理消息的能力,并且這種能力是無(wú)限循環(huán)的,直到被停止為止。

簡(jiǎn)單使用

 public Handler handler;
 public void looperThread(){
     new Thread(new Runnable() {
         @Override
         public void run() {
             Looper.prepare();
             handler = new Handler(Looper.myLooper(),new Handler.Callback() {
                 @Override
                 public boolean handleMessage(Message msg) {
                     Log.e(TAG,"收到發(fā)送過(guò)來(lái)的消息:"+msg.obj.toString());
                     return false;
                 }
             });
             Looper.loop();
         }
     }).start();
 }
 @Override
 public void onClick(View view) {
     Message message = Message.obtain();
     message.obj = "點(diǎn)擊事件消息時(shí)間戳:"+System.currentTimeMillis()%10000;
     handler.sendMessage(message);
 }

創(chuàng)建一個(gè)具有消息循環(huán)的線程,該線程中創(chuàng)建一個(gè)和該looper綁定的handler對(duì)象,然后點(diǎn)擊事件中不斷的去發(fā)送消息給looper循環(huán),看下最后的效果如下:

18:17:45.459 12495-12538/com.example.myapplication E/[MainActivity]: 收到發(fā)送過(guò)來(lái)的消息:點(diǎn)擊事件消息時(shí)間戳:5458
18:17:45.690 12495-12538/com.example.myapplication E/[MainActivity]: 收到發(fā)送過(guò)來(lái)的消息:點(diǎn)擊事件消息時(shí)間戳:5690
18:17:45.887 12495-12538/com.example.myapplication E/[MainActivity]: 收到發(fā)送過(guò)來(lái)的消息:點(diǎn)擊事件消息時(shí)間戳:5886
...省略
18:18:40.010 12495-12538/com.example.myapplication E/[MainActivity]: 收到發(fā)送過(guò)來(lái)的消息:點(diǎn)擊事件消息時(shí)間戳:9
18:18:40.840 12495-12538/com.example.myapplication E/[MainActivity]: 收到發(fā)送過(guò)來(lái)的消息:點(diǎn)擊事件消息時(shí)間戳:839
18:18:41.559 12495-12538/com.example.myapplication E/[MainActivity]: 收到發(fā)送過(guò)來(lái)的消息:點(diǎn)擊事件消息時(shí)間戳:1558

可以看到我一直點(diǎn)擊,一直有消息可以被處理,那么說(shuō)明我創(chuàng)建的線程是一直運(yùn)行的,并沒有結(jié)束。那么looper具體是怎么實(shí)現(xiàn)的這樣的功能的呢?

從源碼了解loop原理

在分析源碼之前,先看下整體的類圖關(guān)系:

loop分析

我們從Looper.prepare();這句代碼開始分析:

Looper.prepare();
public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
    final Thread mThread;
    ...省略
    public static void prepare() {
        prepare(true);
    }
  	...省略

可以看到調(diào)用了prepare()方法后,接著調(diào)用了有參函數(shù)prepare:

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

sThreadLocal的泛型參數(shù)是Looper,那么知道Looper保存在了線程所持有的map容器中,首先就是判斷sThreadLocal.get()是否為空,這個(gè)方法在上一章說(shuō)過(guò),是根據(jù)當(dāng)前線程來(lái)獲取的,如果這個(gè)prepare方法在ui線程中調(diào)用那么返回的就是ui線程中的Looper,如果調(diào)用的是子線程中,那么返回的就是子線程的Looper了,如果不為空,拋出異常,意思就是一個(gè)線程只能持有一個(gè)Looper對(duì)象;如果為空的話,那么調(diào)用sThreadLocal的set方法將創(chuàng)建的Looper對(duì)象存放到對(duì)應(yīng)線程的map容器中。

接著調(diào)用了loop函數(shù):

Looper.loop();
    public static void loop() {
        final Looper me = myLooper();
        ...省略
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) { 
                return;
            }  
      		...省略
            try {
                msg.target.dispatchMessage(msg);
            } finally {
         	   ...省略
            } 
 			...省略 
            msg.recycleUnchecked();
        }
    }

大概是這樣的,其中去掉了一些和業(yè)務(wù)無(wú)關(guān)的代碼。

myLooper()

第一步調(diào)用myLooper()方法:

final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

獲取當(dāng)前線程的sThreadLocal中的Looper對(duì)象。從Looper對(duì)象獲取隊(duì)列。

第二步開始for循環(huán),Message msg = queue.next(); // might block 在循環(huán)中不斷的從queue中取Message消息,

獲取msg判斷是否為空,空的話直接返回,不為空的話,調(diào)用msg的Target的dispatchMessage方法。最后msg使用完畢之后就回收msg對(duì)象。

首先來(lái)看下

Message msg = queue.next(); // might block

next()

調(diào)用的是MessageQueue的next方法,代碼如下:

 Message next() {
   		 ...省略
        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) {
                    // 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.
                        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;
                }
 				...省略
            } 
            ...省略
        }
    }

首先調(diào)用nativePollOnce(ptr, nextPollTimeoutMillis); 這個(gè)方法是調(diào)用的native方法,意思就是阻塞當(dāng)前線程,在延遲nextPollTimeoutMillis時(shí)長(zhǎng)后喚醒當(dāng)前線程。

接著調(diào)用:

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

其中的判斷是msg.target == null這個(gè)條件,這個(gè)條件說(shuō)明當(dāng)前的msg是沒有設(shè)置Target的,msg的Target一般是handler,如果這里是空的話,那么這個(gè)msg就是同步屏障消息,用于攔截同步消息的,讓異步消息有優(yōu)先處理權(quán)。如果當(dāng)前是同步屏障的話,那么while循環(huán),一直向后遍歷msg節(jié)點(diǎn),條件是這個(gè)msg非空和非異步消息,所以這里能夠跳出循環(huán)的情況就是msg到了尾部為空了,要么就是向后遍歷發(fā)現(xiàn)了異步消息。接著往下看:

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

分為兩種情況:

(1)如果msg為空的話,先設(shè)置延遲時(shí)長(zhǎng)nextPollTimeoutMillis = -1;接著這趟for循環(huán)結(jié)束,回到起點(diǎn)的位置,又開始執(zhí)行nativePollOnce(ptr, nextPollTimeoutMillis);延遲時(shí)間是-1那么線程就會(huì)阻塞下去,直到被喚醒,不會(huì)執(zhí)行for循環(huán)了(msg在進(jìn)入隊(duì)列的時(shí)候會(huì)去喚醒線程的,所以這里不會(huì)一直阻塞的)。

(2)如果msg不為空的話,假設(shè)消息設(shè)置的時(shí)間點(diǎn)大于現(xiàn)在的時(shí)間點(diǎn),那么設(shè)置nextPollTimeoutMillis 為時(shí)間差和整數(shù)最大值中的最小值。這樣的話,線程在下次循環(huán)中的開頭就會(huì)阻塞到可以執(zhí)行該消息的when時(shí)間節(jié)點(diǎn)再次運(yùn)行(線程在阻塞的時(shí)候不會(huì)去輪轉(zhuǎn)cpu時(shí)間片所以可以節(jié)約cpu資源,同樣的,如果阻塞期間有消息進(jìn)來(lái)可以馬上運(yùn)行,那么還是會(huì)被喚醒的);假設(shè)消息設(shè)置的時(shí)間點(diǎn)小于現(xiàn)在的時(shí)間點(diǎn),那么從msg消息鏈中把該消息摘取出來(lái),msg標(biāo)記為使用中,將msg返回。

思考:隊(duì)列中頭部msg是同步屏障的話,那么優(yōu)先從前往后去查找異步消息進(jìn)行處理,所以在同步屏障消息之后的同步消息不會(huì)被執(zhí)行,直到被移除為止。隊(duì)列頭部是普通的消息的時(shí)候,是根據(jù)when時(shí)間節(jié)點(diǎn)來(lái)判斷,是直接返回msg,還是等待when-now時(shí)間差在去循環(huán)一遍查找頭結(jié)點(diǎn)msg。

handler.dispatchMessage

handler = new Handler(Looper.myLooper(),new Handler.Callback() {
	           @Override
	           public boolean handleMessage(Message msg) {
	               Log.e(TAG,"收到發(fā)送過(guò)來(lái)的消息:"+msg.obj.toString());
	               return false;
	           }
	       });

handler在創(chuàng)建的參數(shù)是Looper和Callback,接著再來(lái)看下dispatchMessage是如何實(shí)現(xiàn)的:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
private static void handleCallback(Message message) {
    message.callback.run();
}

如果msg存在callback的話,直接調(diào)用callbakc的run方法,這里不存在我們傳遞msg沒有設(shè)置callback,那么走下面的那個(gè)邏輯,我們給handler設(shè)置了mCallback,那么就直接回調(diào)handler的mCallback.handleMessage的方法:

    @Override
    public boolean handleMessage(Message msg) {
        Log.e(TAG,"收到發(fā)送過(guò)來(lái)的消息:"+msg.obj.toString());
        return false;
    }

這樣也就出現(xiàn)了我們開頭demo中的打印消息了。

handler分析

我們通過(guò)上面的next方法分析了如何從隊(duì)列中獲取消息,那么我們還沒有分析消息是如何入隊(duì)的,接下來(lái)我們來(lái)分析下handler的幾個(gè)關(guān)鍵的問題,(1)handler的消息一個(gè)分為幾種;(2)handler發(fā)送消息到哪去了。

我們從handler的構(gòu)造函數(shù)入手:

handler = new Handler(Looper.myLooper(),new Handler.Callback() {
               @Override
               public boolean handleMessage(Message msg) {
                   Log.e(TAG,"收到發(fā)送過(guò)來(lái)的消息:"+msg.obj.toString());
                   return false;
               }
           });
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

我們可以看到,handler一共持有四個(gè)關(guān)鍵變量,Looper循環(huán)(和looper關(guān)聯(lián),handler發(fā)送的消息只會(huì)發(fā)到這個(gè)隊(duì)列中),mQueue 持有Looper的隊(duì)列,mCallback 用于處理消息的回調(diào)函數(shù),mAsynchronous 標(biāo)志這個(gè)handler發(fā)送的消息是同步的還是異步的。

我們?cè)賮?lái)看一下消息是怎么發(fā)送的:

Message message = Message.obtain();
message.obj = "點(diǎn)擊事件消息時(shí)間戳:"+System.currentTimeMillis()%10000;
handler.sendMessage(message);

首先從Message中獲取一個(gè)message,這個(gè)Message其實(shí)里面保存著msg的鏈表,遍歷鏈表,返回的是回收的msg,其中flags整數(shù)變量標(biāo)志著msg是否正在使用中,是否是異步消息等等狀態(tài)。

handler.sendMessage(message);

然后使用handler去發(fā)送一個(gè)msg對(duì)象、接著進(jìn)去看下:

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

msg初始狀態(tài)下是同步消息,sendMessage方法發(fā)送出去的消息delayMillis 延遲時(shí)間是0;

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
     msg.target = this;
     if (mAsynchronous) {
         msg.setAsynchronous(true);
     }
     return queue.enqueueMessage(msg, uptimeMillis);
 }

在入隊(duì)列之前,將msg的Target設(shè)置為當(dāng)前handler,然后根據(jù)handler是否是異步的,設(shè)置msg是否是異步的,然后調(diào)用隊(duì)列的入隊(duì)函數(shù),將消息入隊(duì)。

這里先回答第二個(gè)問題,如何入隊(duì)的:

消息入隊(duì)

  boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            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;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

首先判斷當(dāng)前入隊(duì)msg的when時(shí)間是否比隊(duì)列中的頭結(jié)點(diǎn)的when時(shí)間節(jié)點(diǎn)靠前,靠前的話,就將入隊(duì)的msg加入到隊(duì)列的頭部,并且調(diào)用nativeWake(mPtr);方法喚醒looper所在的線程,那么next()開始執(zhí)行了,可以馬上遍歷隊(duì)列,消耗msg消息。如果當(dāng)前消息msg的時(shí)間節(jié)點(diǎn)when大于頭部節(jié)點(diǎn),首先設(shè)置needWake標(biāo)志, 是否需要喚醒分為:如果隊(duì)列頭部是同步屏障,并且入隊(duì)消息msg是異步消息,那么就需要喚醒線程,其他情況不需要喚醒;接著執(zhí)行for循環(huán),循環(huán)里面尋找隊(duì)列中第一個(gè)節(jié)點(diǎn)時(shí)間是大于msg消息的時(shí)間節(jié)點(diǎn)的(這意味著隊(duì)列中消息是按照時(shí)間節(jié)點(diǎn)排序的),循環(huán)結(jié)束后,將入隊(duì)的msg插入到隊(duì)列中,最后根據(jù)需要是否喚醒線程。

同步屏障

同步屏障功能是讓隊(duì)列中的同步消息暫時(shí)不執(zhí)行,直到同步屏障被移除,異步消息可以不受影響的被執(zhí)行,相當(dāng)于排隊(duì)買票的隊(duì)列中頭部有個(gè)人一直卡著不走,只有vip的人才能正常在窗口中買票,其他普通人買不了票,如果那個(gè)頭部卡著的那個(gè)人不走的話。這個(gè)同步屏障非常有用,用于優(yōu)先執(zhí)行某些任務(wù)。

同步屏障我們使用的比較少,但是安卓frame層代碼有使用這個(gè)同步屏障的功能,例如ViewRootImp中:

ViewRootImp中:
    void scheduleTraversals() { 
   			...省略
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ...省略
    }
Choreographer中:
   	private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {  
        	...省略
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); 
            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }         

向隊(duì)列中發(fā)送一個(gè)同步屏障getQueue().postSyncBarrier();看下源碼如何實(shí)現(xiàn)的:

   public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }
    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 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;
        }
    }

同步屏障的時(shí)間節(jié)點(diǎn)是當(dāng)前時(shí)間,還可以知道同步屏障消息的Target是空的,成員變量arg1保存的是同步屏障的自增值。接下來(lái)就是找到隊(duì)列中第一個(gè)時(shí)間節(jié)點(diǎn)比自己大的節(jié)點(diǎn)位置,然后插入到隊(duì)列中,所以屏障也是按照時(shí)間來(lái)排列的,沒有特殊待遇。

接著使用handler向Looper中發(fā)送了一個(gè)異步消息:

   Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
   msg.arg1 = callbackType;
   msg.setAsynchronous(true);
   mHandler.sendMessageAtTime(msg, dueTime);

可以看到異步消息需要設(shè)置msg.setAsynchronous(true);

執(zhí)行ui的任務(wù)使用異步消息去執(zhí)行,為啥要用異步,因?yàn)樵?.0以上的安卓系統(tǒng)中已經(jīng)開始使用了垂直同步技術(shù)了,所以重繪頁(yè)面的操作需要按照屏幕刷新率來(lái)執(zhí)行,假如一個(gè)16ms里面有多次重繪請(qǐng)求,最終也只會(huì)拋棄掉,只保留一個(gè)重繪消息,所以,為了保證重繪操作能夠在收到同步信號(hào)的時(shí)間節(jié)點(diǎn)馬上執(zhí)行,必須使用同步屏障,這樣前面排隊(duì)的同步消息暫時(shí)不執(zhí)行,優(yōu)先執(zhí)行我們的重繪界面的異步消息,這樣可以保證我們的界面盡量能夠及時(shí)刷新,避免丟幀。、

再來(lái)看下handler.post()方法:

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

可以看到,其實(shí)也是封裝了一個(gè)msg對(duì)象,將callback傳遞給它,我們?cè)赿ispatchMessge函數(shù)中也知道,如果msg如果有自己的callback 就會(huì)調(diào)用這個(gè)回調(diào)處理消息,不會(huì)使用handler自己的callback 來(lái)處理消息。

總結(jié)

根據(jù)以上所說(shuō)的關(guān)系,畫一張圖:

結(jié)論:

handler的消息分為:同步消息,異步消息,屏障消息。

handler的消息發(fā)送:消息都發(fā)送到了和它綁定的Looper的隊(duì)列中去了。

那么queue一對(duì)一looper,looper一對(duì)多handler,looper對(duì)象保存在所在線程的ThreadLocal中。

到此這篇關(guān)于Android Loop機(jī)制中Looper與handler詳細(xì)分析的文章就介紹到這了,更多相關(guān)Android Looper與handler內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論