Android的消息機(jī)制(異步處理)


Android學(xué)習(xí)整理 - 系列


目錄:

Android的消息機(jī)制(異步處理)組成

  1. Message
  2. Handler
  3. 消息隊(duì)列MessageQueue
  4. Looper

AsyncTask
Looper與Handler的關(guān)系(關(guān)鍵)
AsyncTask和Handler對(duì)比
RxAndroid


Android異步處理組成

  • Message
  • Handler
  • MessageQueue
  • Looper

Message

在線程之間傳遞的消息怎静,可以在內(nèi)部攜帶少量的信息泞辐,用于在不同線程之間交換數(shù)據(jù)校赤。比如,在Activity中

private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //這里處理發(fā)過來的信息
            if (msg.what == 1){
                //收到對(duì)應(yīng)的信息開始程序控制邏輯
                Log.d(TAG,"收到風(fēng)篙梢,開始bb");
            }
        }
    };

然后發(fā)送信息

        Message message = new Message();
        message.what = 1;
        mHandler.sendMessage(message);

下面是Message能攜帶的信息類型

Handler

處理者缅叠,只要用于發(fā)送和處理信息。
發(fā)送消息一般用Handler的sendMessage方法欢顷,有時(shí)候會(huì)用帶有AtTime的mHandler.sendMessageAtTime()等方法定時(shí)發(fā)送實(shí)現(xiàn)定時(shí)啟動(dòng)某些功能或更新ui的的功能。最終消息傳到Handler的handleMessage方法處理

消息隊(duì)列MessageQueue

主要用于存放所有通過Handler發(fā)送的的消息捉蚤。這部分消息一直存放于消息隊(duì)列中抬驴,等待被處理炼七。重要的是,每個(gè)線程
只會(huì)有一個(gè)MessageQueue對(duì)象
布持。

Looper

它是每個(gè)線程中MessageQueue的管家豌拙,這個(gè)是很重要的,當(dāng)調(diào)用Looper的loop()方法后题暖,就會(huì)進(jìn)入到一個(gè)無限循環(huán)中按傅,然后 每當(dāng)發(fā)現(xiàn)消息隊(duì)列中存在一條消息就取出,并傳遞到Handler的handleMessage()方法中胧卤。每個(gè)線程只會(huì)有一個(gè)Looper對(duì)象

流程

  • 首先唯绍,在主線程中創(chuàng)建一個(gè)Handler對(duì)象,重寫handleMessage方法枝誊。
  • 當(dāng)主線程需要進(jìn)行UI操作(比如播放器更新SeekBar)况芒,就創(chuàng)建一個(gè)Message對(duì)象,通過Handler把消息發(fā)送出去叶撒。之后這條消息將會(huì)被添加到消息隊(duì)列等待被處理绝骚,并且Looper會(huì)一直嘗試從消息隊(duì)列中取出待處理消息,最后發(fā)到Handler的handleMessage()方法中祠够。

Handler是在主線程中創(chuàng)建的皮壁,所以handleMessage()方法中的代碼也會(huì)在主線程中運(yùn)行。這樣就不用擔(dān)心子線程UI操作出錯(cuò)

異步消息處理機(jī)制流程圖

Android也封裝了上面這個(gè)流程

runOnUiThread(new Runnable() {
            @Override
            public void run() {
                
            }
        });

AsyncTask

Android為了方便我們?cè)谧泳€程中對(duì)UI進(jìn)行操作哪审,提供的工具。原理也是基于上面的異步消息處理機(jī)制流程虑瀑。嗯湿滓,其實(shí)就是封裝的意思。

基本用法
class AsyncTaskDemo extends AsyncTask<Params, Progress, Result>{
        //四個(gè)重載方法
      }

上面的AsyncTask類總共有三個(gè)范型

  • Params 執(zhí)行AsyncTask時(shí)傳入的參數(shù)舌狗,一般用于在后臺(tái)任務(wù)中使用
  • Progress 后臺(tái)任務(wù)如果需要進(jìn)度叽奥,使用這里指定的范型作為進(jìn)度的單位
  • Result 任務(wù)處理完成后痛侍,如果需要返回結(jié)果朝氓,使用這里的范型作為返回值類型

AsyncTask的重載方法

  • onPreExecute()
    在后臺(tái)任務(wù)執(zhí)行前開始調(diào)用,用于界面上的初始化等主届,比如顯示進(jìn)度條對(duì)話框

  • doInBackground(Params...)
    這里的代碼會(huì)之執(zhí)行在子線程赵哲,可以在這里處理所有的耗時(shí)任務(wù),任務(wù)完成可以通過return語句來返回任務(wù)的執(zhí)行結(jié)果君丁,特殊情況下枫夺,AsyncTask的第一個(gè)范型為Void,則不返回任務(wù)的執(zhí)行結(jié)果绘闷。
    警告:這里不能進(jìn)行UI操作橡庞,如果需要反饋當(dāng)前任務(wù)的執(zhí)行進(jìn)度较坛,調(diào)用publishProgress(Progress...)來完成

  • onProgressUpdate(Progress...)

    在這里對(duì)UI進(jìn)行操作
    當(dāng)在doInBackground(Params...)執(zhí)行了publishProgress(Progress...)方法后,這個(gè)方法就會(huì)被調(diào)用扒最,看參數(shù)是一樣的丑勤,就是
    publishProgress(Progress...)傳過來的

  • onPostExecute(Result)

    后臺(tái)任務(wù)執(zhí)行結(jié)束時(shí)被調(diào)用,Result是doInBackground(Params...)返回的數(shù)據(jù)吧趣,我們可以在這里進(jìn)行最后的UI工作法竞,比如,關(guān)閉進(jìn)度條窗口

全部的就像這樣

/**
     *  @param Void 后臺(tái)任務(wù)返回空值 doInBackground()
     *  @param Integer 有進(jìn)度條再菊,用這個(gè)作為進(jìn)度顯示單位 onProgressUpdate()
     *  @param Boolean 用布爾型數(shù)據(jù)反饋執(zhí)行結(jié)果 onPostExecute
     * */
    class AsyncTaskDemo extends AsyncTask<Void, Integer, Boolean>{

        @Override
        protected Boolean doInBackground(Void... params) {
//            后臺(tái)處理耗時(shí)任務(wù)爪喘,比如下載,這里有進(jìn)度
            publishProgress(Progress...);//這個(gè)方法把進(jìn)度發(fā)到onProgressUpdate()處理了
            return null;
        }

        @Override
        protected void onPreExecute() {
//            顯示進(jìn)度對(duì)話框纠拔,任務(wù)開始前
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            //任務(wù)完成時(shí)調(diào)用秉剑,可以響應(yīng)任務(wù)結(jié)束的邏輯(用Result操作)
            //關(guān)閉進(jìn)度條框
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            //更新UI,比如進(jìn)度條值
        }
    }

最后

 new AsyncTaskDemo().execute();

就啟動(dòng)任務(wù)了


Looper與Handler的關(guān)系

轉(zhuǎn)載自 詳解Handler和Looper的關(guān)系

Handler

A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

Looper

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare()
in the thread that is to run the loop, and then loop()
to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler
class.

Handler就是從消息隊(duì)列中拿出消息,Looper是負(fù)責(zé)遍歷和拿取消息隊(duì)列內(nèi)的消息的

Looper源碼

public class Looper {  
    // 每個(gè)線程中的Looper對(duì)象其實(shí)是一個(gè)ThreadLocal稠诲,即線程本地存儲(chǔ)(TLS)對(duì)象  
    private static final ThreadLocal sThreadLocal = new ThreadLocal();  
    // Looper內(nèi)的消息隊(duì)列  
    final MessageQueue mQueue;  
    // 當(dāng)前線程  
    Thread mThread;  
    // 侦鹏。。臀叙。其他屬性  
  
    // 每個(gè)Looper對(duì)象中有它的消息隊(duì)列略水,和它所屬的線程  
    private Looper() {  
        mQueue = new MessageQueue();  
        mRun = true;  
        mThread = Thread.currentThread();  
    }  
  
    // 我們調(diào)用該方法會(huì)在調(diào)用線程的TLS中創(chuàng)建Looper對(duì)象  
    public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            // 試圖在有Looper的線程中再次創(chuàng)建Looper將拋出異常  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper());  
    }  
    // 其他方法  
}  

可以看到Looper的構(gòu)造函數(shù)Looper()被聲明為private,也就是說劝萤,在外部渊涝,我們不能直接的使用Looper的構(gòu)造函數(shù)。所以床嫌,它這么創(chuàng)建Looper對(duì)象跨释,看下面

public static final void prepare() {  
       if (sThreadLocal.get() != null) {  
           // 試圖在有Looper的線程中再次創(chuàng)建Looper將拋出異常  ,只能有一個(gè)
           throw new RuntimeException("Only one Looper may be created per thread");  
       }  
        //創(chuàng)建Looper對(duì)象
       sThreadLocal.set(new Looper());  
   }  

這個(gè)方法系統(tǒng)會(huì)在ThreadLocal內(nèi)添加一個(gè)Looper對(duì)象厌处,并且只能有一個(gè)Looper對(duì)象鳖谈,接著

public static final void loop() {  
       Looper me = myLooper();  //得到當(dāng)前線程Looper  
       MessageQueue queue = me.mQueue;  //得到當(dāng)前l(fā)ooper的MQ  
         
       // 這兩行沒看懂= = 不過不影響理解  
       Binder.clearCallingIdentity();  
       final long ident = Binder.clearCallingIdentity();  
       // 開始循環(huán)  
       while (true) {  
           Message msg = queue.next(); // 取出message  
           if (msg != null) {  
               if (msg.target == null) {  
                   // message沒有target為結(jié)束信號(hào),退出循環(huán)  
                   return;  
               }  
               // 日志阔涉。缆娃。。  
               if (me.mLogging!= null) me.mLogging.println(  
                       ">>>>> Dispatching to " + msg.target + " "  
                       + msg.callback + ": " + msg.what  
                       );  
               // 非常重要瑰排!將真正的處理工作交給message的target贯要,即后面要講的handler  
               msg.target.dispatchMessage(msg);  
               // 還是日志。椭住。郭毕。  
               if (me.mLogging!= null) me.mLogging.println(  
                       "<<<<< Finished to    " + msg.target + " "  
                       + msg.callback);  
                 
               // 下面沒看懂,同樣不影響理解  
               final long newIdent = Binder.clearCallingIdentity();  
               if (ident != newIdent) {  
                   Log.wtf("Looper", "Thread identity changed from 0x"  
                           + Long.toHexString(ident) + " to 0x"  
                           + Long.toHexString(newIdent) + " while dispatching to "  
                           + msg.target.getClass().getName() + " "  
                           + msg.callback + " what=" + msg.what);  
               }  
               // 回收message資源  
               msg.recycle();  
           }  
       }  
   }  

當(dāng)我們調(diào)用Looper.loop()這個(gè)方法后函荣,Looper才真正的開始工作

Looper me = myLooper();  //得到當(dāng)前線程Looper  
MessageQueue queue = me.mQueue;  //得到當(dāng)前l(fā)ooper的MQ  

獲取到當(dāng)前Looper的MessageQueue显押,每一個(gè)Looper都有一個(gè)消息隊(duì)列扳肛,并且這個(gè)消息隊(duì)列作為L(zhǎng)ooper的一個(gè)成員屬性。

然后再while內(nèi)循環(huán)這個(gè)消息隊(duì)列乘碑,不斷的取出數(shù)據(jù)

Message msg = queue.next(); // 取出message  

判斷該消息隊(duì)列是否到結(jié)尾

if (msg != null)  
if (msg.target == null) {  
      // message沒有target為結(jié)束信號(hào)挖息,退出循環(huán)  
      return;  
}  

最后注意下

looper()里有一句很關(guān)鍵

msg.target.dispatchMessage(msg)

其實(shí)是調(diào)用了

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //這里
            handleMessage(msg);
        }
    }

恩。關(guān)鍵在handleMessage(msg);很熟悉吧


AsyncTask和Handler對(duì)比

AsyncTask

  • 優(yōu)點(diǎn):簡(jiǎn)單兽肤,快捷套腹,過程可控
  • 缺點(diǎn):使用多個(gè)異步操作并進(jìn)行UI變更時(shí),將會(huì)變復(fù)雜

Handler

  • 優(yōu)點(diǎn):結(jié)構(gòu)清晰资铡,功能定義明確电禀,適合多個(gè)后臺(tái)任務(wù),
  • 缺點(diǎn):?jiǎn)蝹€(gè)后臺(tái)異步處理笤休,代碼過多(相對(duì)AsyncTask)

RxAndroid

....恕我直言尖飞,不是針對(duì)上面某一個(gè)人........


RxAndroid:上面的Handler。AsyncTask都是辣雞

優(yōu)點(diǎn):可以更好的處理內(nèi)存泄露問題, 代碼也更加優(yōu)雅和可讀, 選擇執(zhí)行線程和監(jiān)聽線程也更加方便店雅。

建設(shè)中政基。插眼


Handler與AsyncTask參考自郭霖《第一行代碼》

晚安

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市闹啦,隨后出現(xiàn)的幾起案子沮明,更是在濱河造成了極大的恐慌,老刑警劉巖窍奋,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荐健,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡琳袄,警方通過查閱死者的電腦和手機(jī)江场,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挚歧,“玉大人,你說我怎么就攤上這事吁峻』海” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵用含,是天一觀的道長(zhǎng)矮慕。 經(jīng)常有香客問我,道長(zhǎng)啄骇,這世上最難降的妖魔是什么痴鳄? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮缸夹,結(jié)果婚禮上痪寻,老公的妹妹穿的比我還像新娘螺句。我一直安慰自己,他們只是感情好橡类,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布蛇尚。 她就那樣靜靜地躺著,像睡著了一般顾画。 火紅的嫁衣襯著肌膚如雪取劫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天研侣,我揣著相機(jī)與錄音谱邪,去河邊找鬼。 笑死庶诡,一個(gè)胖子當(dāng)著我的面吹牛惦银,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灌砖,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼璧函,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了基显?” 一聲冷哼從身側(cè)響起蘸吓,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撩幽,沒想到半個(gè)月后库继,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窜醉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年宪萄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榨惰。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拜英,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琅催,到底是詐尸還是另有隱情居凶,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布藤抡,位于F島的核電站侠碧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缠黍。R本人自食惡果不足惜弄兜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧替饿,春花似錦语泽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腾夯,卻和暖如春颊埃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蝶俱。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工班利, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人榨呆。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓罗标,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親积蜻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闯割,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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