Android中Looper、Handler炊汤、Thread正驻、Message的分析

關(guān)于這幾個(gè)技術(shù)術(shù)語(yǔ)弊攘,讓我們從Android的實(shí)際使用中來(lái)漸漸理清它們。

1. Looper的初始化

Application被創(chuàng)建時(shí),ActivityThread的main函數(shù)會(huì)被調(diào)用,其中初始化了一個(gè)Looper

Looper.prepareMainLooper();

其實(shí)現(xiàn)在Looper.java中:

    /**
     * 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); //進(jìn)行初始化
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper(); //賦值
        }
    }

prepare(false)是對(duì)Looper進(jìn)行初始化, 初始化之后調(diào)用將Looper對(duì)象賦值給sMainLooper. 所以我們只需要看prepare函數(shù):

    ......
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal();
    ......

    private static void prepare(boolean quitAllowed) {
        if(sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        } else {
            sThreadLocal.set(new Looper(quitAllowed)); //創(chuàng)建一個(gè)Looper對(duì)象,放入ThreadLocal中
        }
    }

從之前的描述可以看出,Looper是一個(gè)單例,所以其構(gòu)造函數(shù)是private的:

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

2. 初始化MessageQueue

上面的Looper的構(gòu)造函數(shù)中又初始化了一個(gè)MessageQueue姑曙,這里又引出了一個(gè)重要的對(duì)象MessageQueue . 每個(gè)Looper都保持一個(gè)MessageQueue用來(lái)存放Message. 這里只需要記住Looper和MessageQueue的關(guān)系即可.下面是MessageQueue 的構(gòu)造函數(shù):

/**
 * Low-level class holding the list of messages to be dispatched by a
 * Looper.  Messages are not added directly to a MessageQueue,
 * but rather through  Handler objects associated with the Looper.
 * 
 * You can retrieve the MessageQueue for the current thread with
 *  Looper.myQueue().
 */


    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

從構(gòu)造函數(shù)上面的描述可以看到,Message是不能直接加入到MessageQueue中的, 必須通過(guò)Handler來(lái)添加. 這里你可能會(huì)有疑問(wèn)襟交,為什么Message不能直接加入到MessageQueue里面呢?帶著這個(gè)問(wèn)題我們繼續(xù)向下看伤靠。

3. 創(chuàng)建ActivityThread對(duì)象

Looper初始化完成后, 回到ActivityThread的main函數(shù), 這里還創(chuàng)建了一個(gè)ActivityThread對(duì)象,對(duì)App來(lái)說(shuō),ActivityThread是一個(gè)非常重要的類(lèi),

        //初始化ActivityThread
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

ActivityThread的構(gòu)造函數(shù)中初始化了ResourcesManager,保存在mResourcesManager中. thread.attach(false)這個(gè)比較重要,是進(jìn)行一些狀態(tài)的設(shè)置和初始化. 隨后會(huì)初始化這個(gè)應(yīng)用程序UI線(xiàn)程的Handle .下面是getHandler()

    final Handler getHandler() {
        return mH;
    }

其中mH的定義如下:

private class H extends Handler {
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                } break;
                case RELAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                    handleRelaunchActivity(r);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        }
}

其handleMessage這個(gè)函數(shù)很長(zhǎng),仔細(xì)看就可以發(fā)現(xiàn),這里調(diào)度了應(yīng)用開(kāi)發(fā)中各個(gè)組件的生命周期.包括activityRestart/activityPause/activityStop/activityDestroy/activityNewIntent等等.

4. 執(zhí)行消息循環(huán)

隨后main函數(shù)會(huì)調(diào)用Looper.loop(),其定義如下:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        ......
        final MessageQueue queue = me.mQueue;
        ......
        for (;;) {
            Message msg = queue.next(); // might block
            .......
            msg.target.dispatchMessage(msg);
            ......
            msg.recycleUnchecked();
        }
    }

可以看到其中調(diào)用了一個(gè)死循環(huán)for (;;),在循環(huán)開(kāi)始調(diào)用

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

來(lái)從MessageQueue中獲取Message來(lái)進(jìn)行處理.注意到MessageQueue的next函數(shù)會(huì)調(diào)用this.nativePollOnce(this.mPtr, nextPollTimeoutMillis); 這是一個(gè)阻塞方法. 會(huì)調(diào)用到Native層的Looper的一些方法. 當(dāng)有消息的時(shí)候,就會(huì)返回一個(gè)Message.

這時(shí)候一個(gè)Activity的Handle/ Looper / MessageQueue都已經(jīng)跑起來(lái)了.他們直接的關(guān)系也清楚了.

5. 自己創(chuàng)建的Handler與Looper和MessageQueue之間的關(guān)系

在Android開(kāi)發(fā)中,我們一般會(huì)自己創(chuàng)建一個(gè)Handler,然后發(fā)送Message進(jìn)行通信 (典型場(chǎng)景就是創(chuàng)建一個(gè)子線(xiàn)程, 在其中執(zhí)行耗時(shí)較多的操作,然后發(fā)一個(gè)Message通知UI更新)

第一種寫(xiě)法:無(wú)參數(shù)的Handler

    Handler defaultHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_HELLO:
                    Log.w(TAG, "defaultHandler ThreadName = "
                            + defaultHandler.getLooper().getThread().getName());
            }
        }
    };
......

//使用
        new Thread(){
            @Override
            public void run() {
                defaultHandler.sendEmptyMessage(MSG_HELLO);
            }
        }.start();

其打印結(jié)果如下:

Thread Name

這種寫(xiě)法創(chuàng)建的Handler,將自動(dòng)與當(dāng)前運(yùn)行的線(xiàn)程相關(guān)聯(lián),也就是說(shuō)這個(gè)自定義的Handler將與當(dāng)前運(yùn)行的線(xiàn)程使用同一個(gè)消息隊(duì)列,并且可以處理該隊(duì)列中的消息.如果不太理解這個(gè)可以查看Handler的構(gòu)造函數(shù):

    public Handler(Callback callback, boolean async) {
        ......
        mLooper = Looper.myLooper();
        ......
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

可以看到, Handler中的mLooper和mQueue就是之前ActivityThread中初始化好的.由于Looper是單例,也就是說(shuō)mLooper和mQueue都只有一個(gè), 而Handler的sendMessage最終會(huì)調(diào)用sendMessageAtTime:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        ......
        return enqueueMessage(queue, msg, uptimeMillis);
    }

看到這里,應(yīng)該對(duì)自己創(chuàng)建的Handler和MessageQueue以及Looper直接的關(guān)系更清楚了.

第二種寫(xiě)法:參數(shù)包含Looper的Handler

Handler有好幾個(gè)構(gòu)造函數(shù),其中如果參數(shù)中不包含Looper,則最終會(huì)進(jìn)入第一種寫(xiě)法中的那個(gè)構(gòu)造函數(shù),如果參數(shù)中包含Looper,則會(huì)進(jìn)入到三個(gè)參數(shù)的構(gòu)造函數(shù)中:

    /**
     * Use the provided Looper instead of the default one and take a callback
     * interface in which to handle messages.  Also set whether the handler
     * should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with represent to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by MessageQueue#enqueueSyncBarrier(long).
     *
     * @param looper The looper, must not be null.
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls  Message#setAsynchronous(boolean) for
     * each Message that is sent to it or Runnable that is posted to it.
     *
     * @hide
     */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

這種Handler初始化的時(shí)候必須創(chuàng)建一個(gè)Looper對(duì)象

    Handler mHandler;

    class CustomThread extends Thread {

        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case MSG_HELLO:
                            Log.w(TAG, "defaultHandler ThreadName = "
                            + defaultHandler.getLooper().getThread().getName());
                            break;
                        default:
                            break;
                    }
                }
            };
            Looper.loop();
        }
    }

//使用
        CustomThread mCustomThread = new CustomThread();
        mCustomThread.start();

我給TextView加了一個(gè)點(diǎn)擊事件捣域,點(diǎn)擊會(huì)打印對(duì)應(yīng)的線(xiàn)程名:

        appNameText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendEmptyMessage(MSG_HELLO);
                Log.w(TAG, "MainThreadName =" + getMainLooper().getThread().getName());
            }
        });

其結(jié)果如下:

Different Thread

通過(guò)上面兩個(gè)例子,你應(yīng)該對(duì)Thread/Looper/MessageQueue/Handler有更深入的了解了.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宴合,隨后出現(xiàn)的幾起案子焕梅,更是在濱河造成了極大的恐慌,老刑警劉巖卦洽,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贞言,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡阀蒂,警方通過(guò)查閱死者的電腦和手機(jī)该窗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蚤霞,“玉大人酗失,你說(shuō)我怎么就攤上這事∶列澹” “怎么了级零?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)滞乙。 經(jīng)常有香客問(wèn)我奏纪,道長(zhǎng),這世上最難降的妖魔是什么斩启? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任序调,我火速辦了婚禮,結(jié)果婚禮上兔簇,老公的妹妹穿的比我還像新娘发绢。我一直安慰自己,他們只是感情好垄琐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布边酒。 她就那樣靜靜地躺著,像睡著了一般狸窘。 火紅的嫁衣襯著肌膚如雪墩朦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天翻擒,我揣著相機(jī)與錄音氓涣,去河邊找鬼牛哺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛劳吠,可吹牛的內(nèi)容都是我干的引润。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼痒玩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼淳附!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蠢古,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奴曙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后便瑟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體缆毁,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡番川,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年到涂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颁督。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡践啄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沉御,到底是詐尸還是另有隱情屿讽,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布吠裆,位于F島的核電站伐谈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏试疙。R本人自食惡果不足惜诵棵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祝旷。 院中可真熱鬧履澳,春花似錦、人聲如沸怀跛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吻谋。三九已至忠蝗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漓拾,已是汗流浹背什湘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工长赞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闽撤。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓得哆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哟旗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贩据,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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