Android 跨線程通信(Handler)

1.進程與線程

  • 進程:android一般一個程序占有一個進程棺滞,但可以通過給四大組件指定android:process屬性 開啟單獨,一個進程會運行在
  • 線程: 在一個應(yīng)用 中耗時操作一般要開啟子線程去操作累盗,也就是說一個進程可以有多個線程狼忱,它們之間是包含關(guān)系扭仁。
  • 子進程和父進程有不同的代碼和數(shù)據(jù)空間讼油,而多個線程則共享數(shù)據(jù)空間,每個線程有自己的執(zhí)行堆棧和程序計數(shù)器為其執(zhí)行上下文颤难。
  • 進程間相互獨立神年,同一進程的各線程間共享。某進程內(nèi)的線程在其它進程不可見行嗤。
  • 進程間通信IPC已日,線程間可以直接讀寫進程數(shù)據(jù)段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數(shù)據(jù)的一致性栅屏。
  • 線程間通信除了Handler這種外飘千,還有其他幾種方式:管道Pip堂鲜、共享內(nèi)存、通過文件及數(shù)據(jù)庫等

2.通信

我們使用一個Handler進行通信护奈,線程A缔莲、B和C、D之間霉旗,只有一個可以接收消息并處理痴奏,另一個只能夠發(fā)送。


多線程通信1.png

3.消息處理中的幾大角色

圖片引用地址:http://www.reibang.com/p/7657f541c461

image

andriod提供了Handler和Looper來滿足線程間的通信厌秒。Handler先進先出原則读拆。Looper類用來管理特定線程內(nèi)對象之間的消息交換

Message

  • 線程間通信就是在傳遞消息,Message就是消息的載體鸵闪。常用的有四個字段:arg1檐晕,arg2,what蚌讼,obj辟灰。obj可以攜帶Object對象,其余三個可以攜帶整形數(shù)據(jù)篡石。

MessageQueue

  • MessageQueue是持有Message(在Looper中派發(fā))的一個鏈表芥喇,Message并不是直接添加到MessageQueue中的,而是通過與Looper相關(guān)聯(lián)的Handler來進行的凰萨。
  • 用來存放線程放入的消息,讀取會自動刪除消息乃坤,單鏈表維護,在插入和刪除上有優(yōu)勢沟蔑。在其next()中會無限循環(huán),不斷判斷是否有消息狱杰,有就返回這條消息并移除瘦材。

Looper

  • 一個線程可以產(chǎn)生一個Looper對象,由它來管理此線程里的MessageQueue
  • Looper創(chuàng)建的時候會創(chuàng)建一個MessageQueue仿畸,調(diào)用loop()方法的時候消息循環(huán)開始食棕,loop()也是一個死循環(huán),會不斷調(diào)用messageQueue的next()错沽,當(dāng)有消息就處理簿晓,否則阻塞在messageQueue的next()中。當(dāng)Looper的quit()被調(diào)用的時候會調(diào)用messageQueue的quit(),此時next()會返回null千埃,然后loop()方法也跟著退出憔儿。
  • MessageQueue和Looper是一對一關(guān)系,Handler和Looper是多對一
  • 線程是默認沒有Looper的放可,線程需要通過Looper.prepare()谒臼、綁定Handler到Looper對象朝刊、Looper.loop()來建立消息循環(huán);在主線程蜈缤,也就是ActivityThread拾氓,在被創(chuàng)建的時候就會初始化Looper,所以主線程中可以默認使用Handler

Handler

  • 在主線程構(gòu)造一個Handler底哥,與Looper溝通咙鞍,以便push新消息到MessageQueue里;
  • 接收Looper從MessageQueue取出Handler所送來的消息。然后在其他線程調(diào)用sendMessage(),此時主線程的MessageQueue中會插入一條message趾徽,然后被Looper使用.
  • 用于發(fā)送和處理消息的發(fā)送消息续滋,一般使用sendMessage(),還有sendMessage的方法,但最終都是調(diào)用sendMessageAtTime()方法附较,除了sendMessageAtFrontOfQueue()這個方法吃粒。
  • 只要在Looper線程(就是實現(xiàn)了Looper的線程)構(gòu)建Handler類,那么這個Handler實例就獲取該Looper線程MessageQueue實例的引用拒课,Handler 在sendMessage()的時候就通過這個引用往消息隊列里插入新消息徐勃。

Thread

  • UIthread 通常就是main thread,而Android啟動程序時會替它建立一個MessageQueue,系統(tǒng)的主線程在ActivityThread的main()為入口開啟主線程早像,其中定義了一系列消息類型僻肖,包含四大組件的啟動停止。

ThreadLocal

  • ThreadLocal的作用是提供線程內(nèi)的局部變量卢鹦,這種變量在線程的生命周期內(nèi)起作用臀脏,減少同一個線程內(nèi)多個函數(shù)或者組件之間一些公共變量的傳遞的復(fù)雜度。引用 Android關(guān)于ThreadLocal的思考和總結(jié)

4.創(chuàng)建Handler的兩種方式

一個是在主線程中創(chuàng)建

public class TestActivity extends AppCompatActivity {
        private static final String TAG = "TestActivity";
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //獲得發(fā)送Message對象冀自,進行UI操作
                Log.e(TAG,"------------> msg.what = " + msg.what);
            }
        };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
            initThread();
        }

        private void initThread() {
            //開啟線程處理耗時操作
            new Thread(new Runnable() {
                @Override
                public void run() {
                    SystemClock.sleep(2000);
                    //通過Handler發(fā)送消息切換回主線程(mHandler所在的線程)
                    mHandler.sendEmptyMessage(0);
                }
            }).start();
        }
  • 一個是在普通工作線程中創(chuàng)建
public class TestActivity extends AppCompatActivity {
        private static final String TAG = "TestActivity";
        //主線程的Handler
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //獲得發(fā)送Message對象揉稚,進行UI操作
                Log.e(TAG,"------------> msg.what = " + msg.what);
            }
        };
        //子線程中的Handler
        private Handler mHandlerThread = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
            initThread();
        }

        private void initThread() {
            //開啟線程處理耗時的操作
            new Thread(new Runnable() {
                @Override
                public void run() {
                    SystemClock.sleep(2000);
                    //通過Handler發(fā)送消息切換回主線程(mHandler所在的線程)
                    mHandler.sendEmptyMessage(0);
                    //調(diào)用Looper.prepare()方法
                    Looper.prepare();
                    mHandlerThread = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("sub thread","---------> msg.what = " + msg.what);
                        }
                    };
                    mHandlerThread.sendEmptyMessage(1);
                    //調(diào)用Looper.loop()方法
                   Looper.loop();
                }
            }).start();
        }
}
  • 當(dāng)Looper.prepare()執(zhí)行之后,普通的工作線程就變成了Looper線程熬粗,該線程就可以接收并處理消息
  • 而Looper.loop()方法就是進入一個無限循環(huán)搀玖,不斷從MessageQueue中獲取消息,當(dāng)沒有消息時就阻塞在那里
  • Handler 對象在哪個線程下構(gòu)建(構(gòu)造函數(shù)在哪個線程下調(diào)用)驻呐,那么Handler 就會持有該線程的Looper引用和消息隊列的引用灌诅。這個Handler對象可在任意其他線程給該線程的消息隊列添加消息,說明Handler的handlerMessage 也在該線程執(zhí)行的含末。如果該線程不是Looper線程猜拾,在這個線程new Handler 就會報錯!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末佣盒,一起剝皮案震驚了整個濱河市挎袜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖宋雏,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芜飘,死亡現(xiàn)場離奇詭異,居然都是意外死亡磨总,警方通過查閱死者的電腦和手機嗦明,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚪燕,“玉大人娶牌,你說我怎么就攤上這事」菽桑” “怎么了诗良?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲁驶。 經(jīng)常有香客問我鉴裹,道長,這世上最難降的妖魔是什么钥弯? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任径荔,我火速辦了婚禮,結(jié)果婚禮上脆霎,老公的妹妹穿的比我還像新娘总处。我一直安慰自己,他們只是感情好睛蛛,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布鹦马。 她就那樣靜靜地躺著,像睡著了一般忆肾。 火紅的嫁衣襯著肌膚如雪荸频。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天客冈,我揣著相機與錄音试溯,去河邊找鬼。 笑死郊酒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的键袱。 我是一名探鬼主播燎窘,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蹄咖!你這毒婦竟也來了褐健?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚜迅,沒想到半個月后舵匾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡谁不,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年坐梯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刹帕。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吵血,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出偷溺,到底是詐尸還是另有隱情蹋辅,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布挫掏,位于F島的核電站侦另,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏尉共。R本人自食惡果不足惜褒傅,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爸邢。 院中可真熱鬧樊卓,春花似錦、人聲如沸杠河。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽券敌。三九已至唾戚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間待诅,已是汗流浹背叹坦。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卑雁,地道東北人募书。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像测蹲,于是被迫代替她去往敵國和親莹捡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

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