Handler源碼學(xué)習(xí)(一)流程

本系列筆記是我閱讀Handler及相關(guān)類的源碼時(shí)所記錄的一些之前所不知道的知識點(diǎn),該系列分為三部分艺蝴,整體流程凸椿,Message對象回收原理肠槽,MessageQueue管理隊(duì)列
Handler源碼學(xué)習(xí)(一)流程
Handler源碼學(xué)習(xí)(二)Message對象池
Handler源碼學(xué)習(xí)(三)MessageQueue入隊(duì)插隊(duì)

1.創(chuàng)建handler — 默認(rèn)構(gòu)造方法會獲取當(dāng)前線程的looper乡小,也可以傳入指定的looper

/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 *
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 */
public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
        //警告潛在內(nèi)存泄漏風(fēng)險(xiǎn)
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
          //如果是匿名類阔加,內(nèi)部類等等并且不是static 有內(nèi)存泄露風(fēng)險(xiǎn)
            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());
            }
        }
        //創(chuàng)建looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

2.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);
}
//用ThreadLocal來存放looper 如果當(dāng)前線程已經(jīng)有一個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));
}

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

//主線程的looper
/**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
//構(gòu)造方法,創(chuàng)建了一個消息隊(duì)列
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

3.sendMessage

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);
}
//handler中將message添加到消息隊(duì)列
//將這個message的target設(shè)置成當(dāng)前的這個handler
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

4.loop() — 取出消息

//target是handler满钟,這里是個無限循環(huán)
msg.target.dispatchMessage(msg);

5.dispatchMessage() — 分發(fā)消息

//三種情況胜榔,
//第一種Message對象的callback不為空(runnable),交給callback處理湃番,第一種大多是使用post方法傳入runnable對象時(shí)會調(diào)用
//第二種是handler的callback不為空夭织,交給callback處理,callback
//前兩種都沒有的情況下交給handleMessag去處理
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();
    }

public interface Callback {
        public boolean handleMessage(Message msg);
    }
//Handler的callback可以在構(gòu)造方法中傳入
//Message的callback可以在obtain方法中作為參數(shù)傳入吠撮,注意尊惰,一個消息被使用完畢后會被recycle,callback也會被移除,所以只能使用一次

6.post — 各種post方法

//注意傳入的runnable的run方法會在handler所綁定的looper所在線程中執(zhí)行弄屡,最終還是sendMessageDelayed()
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

//不被推薦使用的方法,很容易導(dǎo)致消息隊(duì)列清空戴卜,排序問題,以及其它副作用
//Posts a message to an object that implements Runnable.
//Causes the Runnable r to executed on the next iteration through the
//message queue.
//This method is only for use in very special circumstances -- it
//can easily starve the message queue, cause ordering problems, or have
//other unexpected side-effects.</b>
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;
        }
        return enqueueMessage(queue, msg, 0);
    }

8.擴(kuò)展

  • runOnUiThread()
//如果當(dāng)前線程是ui線程就直接調(diào)用run方法琢岩,如果不是當(dāng)前線程則通過handler發(fā)送消息
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}
  • view.post()
//首先會判斷這個view是否已經(jīng)attach上了window,如果是的話會使用handler來發(fā)送消息
//否則會加入到消息隊(duì)列中师脂,等到attach了才會一次取出執(zhí)行
public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().post(action);
    return true;
}

7.收獲

1.looper是通過 ThreadLoacal來保存 担孔,保證looper只被創(chuàng)建一次,且跟當(dāng)前線程綁定吃警,一個looper糕篇。

2.handler有多個構(gòu)造方法,并不是必須要重寫handleMessage方法酌心,可以通過傳入callback對象來處理消息拌消,同時(shí)也可以給handler指定looper,因此handler可以給其它線程的looper發(fā)送消息安券。

3.MessageQueue不由handler創(chuàng)建墩崩,而是從looper中獲取,一個looper對應(yīng)有一個MessageQueue侯勉,因此一個looper必然可以對應(yīng)多個handler鹦筹,它們都是往這同一個消息隊(duì)列中發(fā)送消息而已

4.Android中的主線程會調(diào)用Looper.prepareMainLooper() 方法來創(chuàng)建一個looper,因此不需要我們手動創(chuàng)建looper

5.handler發(fā)送延遲消息時(shí)址貌,是通過但前時(shí)間+delayed時(shí)間铐拐,在某個時(shí)間去發(fā)送,因此如果在插入message時(shí)的時(shí)間是0练对,會容易導(dǎo)致排序問題

6.每一個Activity在創(chuàng)建的時(shí)候其實(shí)就已經(jīng)創(chuàng)建了一個handler遍蟋,runOnUiThread方法中使用的就是這個handler

7.view.post除了異步更新ui外還有一個作用,可以判斷當(dāng)前view是否已經(jīng)attach window螟凭,在這個runnable任務(wù)被執(zhí)行時(shí)虚青,可以拿到尺寸等等,避免因?yàn)閐isattach造成異常

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赂摆,一起剝皮案震驚了整個濱河市挟憔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烟号,老刑警劉巖绊谭,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異汪拥,居然都是意外死亡达传,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宪赶,“玉大人宗弯,你說我怎么就攤上這事÷蓿” “怎么了蒙保?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長欲主。 經(jīng)常有香客問我邓厕,道長,這世上最難降的妖魔是什么扁瓢? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任详恼,我火速辦了婚禮,結(jié)果婚禮上引几,老公的妹妹穿的比我還像新娘昧互。我一直安慰自己,他們只是感情好伟桅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布敞掘。 她就那樣靜靜地躺著,像睡著了一般楣铁。 火紅的嫁衣襯著肌膚如雪渐逃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天民褂,我揣著相機(jī)與錄音茄菊,去河邊找鬼。 笑死赊堪,一個胖子當(dāng)著我的面吹牛面殖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哭廉,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脊僚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了遵绰?” 一聲冷哼從身側(cè)響起辽幌,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎椿访,沒想到半個月后乌企,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡成玫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年加酵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拳喻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡猪腕,死狀恐怖冗澈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陋葡,我是刑警寧澤亚亲,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站腐缤,受9級特大地震影響朵栖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柴梆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望终惑。 院中可真熱鬧绍在,春花似錦、人聲如沸雹有。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霸奕。三九已至溜宽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間质帅,已是汗流浹背适揉。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煤惩,地道東北人嫉嘀。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像魄揉,于是被迫代替她去往敵國和親剪侮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容