Handler消息傳遞機制學(xué)習(xí)筆記和demo實現(xiàn)

0,寫在前面

博主在學(xué)習(xí)Handler消息傳遞機制時驶兜,學(xué)習(xí)的方法是通過問題來驅(qū)動理解扼仲,都是關(guān)注度比較高的問題,以下是我在學(xué)習(xí)后的簡單整理抄淑,歡迎吐槽屠凶!

1,Handler是什么肆资?能干嘛矗愧?

Google的Android開發(fā)文檔是這么寫的:
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.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
簡言之,就是android.os.Handler允許我們發(fā)送和處理與線程的MessageQueue相關(guān)的Message和Runnable對象郑原。 每個Handler實例都與單個線程和該線程的消息隊列相關(guān)聯(lián)唉韭。
主要用于:1)消息創(chuàng)建;2)將消息插入到隊列中犯犁;3)處理消費者線程上的消息属愤;4)管理隊列中的消息。

2酸役,為什么需要Handler住诸?

image.png

這張圖片清晰的說明了谷歌工程師為什么要開發(fā)Handler這個類』猎瑁可以看出贱呐,Google工程師開發(fā)它主要是為了解決在非UI線程中更新UI組件比較麻煩的問題。

3入桂,Handler都有哪些特點奄薇?

handler可以分發(fā)Message對象和Runnable對象到主線程中, 每個Handler實例抗愁,都會綁定到創(chuàng)建他的線程中(一般是位于主線程)惕艳,它有兩個作用:
1)安排消息或Runnable 在某個主線程中某個地方執(zhí)行;
2)安排一個動作在不同的線程中執(zhí)行驹愚。
Handler中分發(fā)消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message劣纲,long)
sendMessageDelayed(Message逢捺,long)
以上post類方法允許你排列一個Runnable對象到主線程隊列中,
sendMessage類方法癞季, 允許你安排一個帶數(shù)據(jù)的Message對象到隊列中劫瞳,等待更新倘潜。

4,Handler執(zhí)行的流程圖

image.png

UI線程:就是主線程,系統(tǒng)在創(chuàng)建UI線程的時候會初始化一個Looper對象,同時也會 創(chuàng)建一個與其關(guān)聯(lián)的MessageQueue;

Handler:作用就是發(fā)送與處理信息,如果希望Handler正常工作,在當前線程中要有一個Looper對象

Message:Handler接收與處理的消息對象

MessageQueue:消息隊列,先進先出管理Message,在初始化Looper對象時會創(chuàng)建一個與之關(guān)聯(lián)的MessageQueue;

Looper:每個線程只能夠有一個Looper,管理MessageQueue,不斷地從中取出Message分發(fā)給對應(yīng)的Handler處理志于!

大白話來說涮因,就是當子線程想修改Activity中的UI組件時,我們可以新建一個Handler對象,通過這個對象向主線程發(fā)送信息;而發(fā)送的信息會先到主線程的MessageQueue進行等待,由Looper按先入先出順序取出,再根據(jù)message對象的what屬性分發(fā)給對應(yīng)的Handler進行處理。

5伺绽,如何理解Handler消息處理機制养泡?

消息處理機制本質(zhì):一個線程開啟循環(huán)模式持續(xù)監(jiān)聽并依次處理其他線程給它發(fā)的消息。
簡單來說奈应,就是一個線程開啟一個無限循環(huán)模式澜掩,不斷遍歷自己的消息列表,如果有消息就挨個拿出來做處理杖挣,如果列表沒消息肩榕,自己就堵塞(相當于wait,讓出cpu資源給其他線程)惩妇,其他線程如果想讓該線程做什么事株汉,就往該線程的消息隊列插入消息,該線程會不斷從隊列里拿出消息做處理歌殃。

6乔妈,Looper、Handler挺份、MessageQueue,Message作用和存在的意義褒翰?

  6.1,Looper 
  我們知道一個線程是一段可執(zhí)行的代碼匀泊,當可執(zhí)行代碼執(zhí)行完成后优训,線程生命周期便會終止,線程就會退出各聘,那么做為App的主線程揣非,如果代碼段執(zhí)行完了會怎樣?躲因,那么就會出現(xiàn)App啟動后執(zhí)行一段代碼后就自動退出了早敬,這是很不合理的。所以為了防止代碼段被執(zhí)行完大脉,只能在代碼中插入一個死循環(huán)搞监,那么代碼就不會被執(zhí)行完,然后自動退出镰矿,怎么在在代碼中插入一個死循環(huán)呢琐驴?那么Looper出現(xiàn)了,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會變當前線程變成Looper線程(可以先簡單理解:無限循環(huán)不退出的線程),Looper.loop()方法里面有一段死循環(huán)的代碼绝淡,所以主線程會進入while(true){...}的代碼段跳不出來宙刘,但是主線程也不能什么都不做吧?其實所有做的事情都在while(true){...}里面做了牢酵,主線程會在死循環(huán)中不斷等其他線程給它發(fā)消息(消息包括:Activity啟動悬包,生命周期,更新UI馍乙,控件事件等)布近,一有消息就根據(jù)消息做相應(yīng)的處理,Looper的另外一部分工作就是在循環(huán)代碼中會不斷從消息隊列挨個拿出消息給主線程處理潘拨。

  6.2吊输,MessageQueue 
  MessageQueue 存在的原因很簡單,就是同一線程在同一時間只能處理一個消息铁追,同一線程代碼執(zhí)行是不具有并發(fā)性季蚂,所以需要隊列來保存消息和安排每個消息的處理順序。多個其他線程往UI線程發(fā)送消息琅束,UI線程必須把這些消息保持到一個列表(它同一時間不能處理那么多任務(wù)),然后挨個拿出來處理扭屁,這種設(shè)計很簡單,我們平時寫代碼其實也經(jīng)常這么做涩禀。每一個Looper線程都會維護這樣一個隊列料滥,而且僅此一個,這個隊列的消息只能由該線程處理艾船。

  6.3葵腹,Message 
  想讓主線程做什么事,總要告訴它吧屿岂,總要傳遞點數(shù)據(jù)給它吧践宴,Message就是這個載體。

  6.4爷怀,Handler 
  簡單說Handler用于同一個進程的線程間通信阻肩。Looper讓主線程無限循環(huán)地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的运授,那么怎樣在其他的線程往主線程的隊列里放入消息呢烤惊?其實很簡單,我們知道在同一進程中線程和線程之間資源是共享的吁朦,也就是對于任何變量在任何線程都是可以訪問和修改的柒室,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實例逗宜,就可以往主線程的MessageQueue放入消息雄右,主線程在輪詢的時候就會在主線程處理這個消息剥啤。那么怎么拿到主線程 MessageQueue的實例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統(tǒng)一添加消息和消息的回調(diào)處理不脯,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類刻诊,那么這個Handler實例就獲取主線程MessageQueue實例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;)防楷,Handler 在sendMessage的時候就通過這個引用往消息隊列里插入新消息。Handler 的另外一個作用则涯,就是能統(tǒng)一處理消息的回調(diào)复局。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做,這樣的設(shè)計非常的贊粟判。具體做法就是在隊列里面的Message持有Handler的引用(哪個handler 把它放到隊列里亿昏,message就持有了這個handler的引用),然后等到主線程輪詢到這個message的時候档礁,就來回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法角钩。

7,Handler的用法

 7.1呻澜,消息的創(chuàng)建
  Message obtainMessage(int what, int arg1, int arg2)递礼;
  Message obtainMessage();
  Message obtainMessage(int what, int arg1, int arg2, Object obj)羹幸;
  Message obtainMessage(int what)脊髓;
  Message obtainMessage(int what, Object obj);

  7.2栅受,將消息插入到隊列中
      7.2.1将硝,添加任務(wù)到消息隊列中
      Message obtainMessage(int what, int arg1, int arg2)
      Message obtainMessage()
      Message obtainMessage(int what, int arg1, int arg2, Object obj)
      Message obtainMessage(int what)
      Message obtainMessage(int what, Object obj)

      7.2.2,添加數(shù)據(jù)對象到消息隊列中
      boolean sendMessage(Message msg)
      boolean sendMessageAtFrontOfQueue(Message msg)
      boolean sendMessageAtTime(Message msg, long uptimeMillis)
      boolean sendMessageDelayed(Message msg, long delayMillis)

      7.2.3屏镊,添加數(shù)據(jù)對象到消息隊列中
      boolean sendEmptyMessage(int what)
      boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
      boolean sendEmptyMessageDelayed(int what, long delayMillis)

  7.3依疼,消息的兩種處理形式
      7.3.1,任務(wù)消息
      handler.post(new Runnable() {
         @Override
         public void run() {
           //TODO : Do some operation
         }
      });
      7.3.2闸衫,數(shù)據(jù)消息
        final Handler handler = new Handler() {
           @Override
           public void handleMessage(Message message) {
              //TODO : Get the data from Message and perform opertation accordingly.
           }
       };

      handler.sendMessage(message);

  7.4涛贯,如何跟蹤消息隊列處理?
    Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, TAG));
  Example of tracing a message:

      handler.post(new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "Executing Runnable");
        }
      });
      mHandler.sendEmptyMessage(111);

8蔚出,Handler使用的一個簡單demo實現(xiàn)

Thread + Handler
布局代碼:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="26dp"
android:orientation="vertical"
tools:context="com.example.luolu.handlerwiththreaddemo.ThreadHandlerAndroidExample">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:autoLink="web"
    android:text="http://www.baidu.com/"
    android:textStyle="bold"
    android:textSize="20sp"/>

<Button
    android:id="@+id/start"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="50dp"
    android:text="Start"/>

<ProgressBar
    android:id="@+id/progress"
    style="?android:attr/progressBarStyleHorizontal"
    android:indeterminate="false"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:max="10"
    android:progress="0"/>
<TextView
    android:id="@+id/msg"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

</LinearLayout>

ThreadHandlerAndroidExample.java:
package com.example.luolu.handlerwiththreaddemo;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

/**

  • @author luolu
    */
    public class ThreadHandlerAndroidExample extends AppCompatActivity {

    private Handler handler = new Handler();
    private MyHandlerThread myHandlerThread;

    Button btnStart;
    ProgressBar progressBar;
    TextView textMsg;

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

     progressBar = (ProgressBar)findViewById(R.id.progress);
     textMsg = (TextView)findViewById(R.id.msg);
     btnStart = (Button)findViewById(R.id.start);
    
     myHandlerThread = new MyHandlerThread("myHandlerThread");
     final Runnable myRunnable = new Runnable() {
    
         @Override
         public void run() {
             for (int i = 0; i <= 10; i++) {
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
    
                 //is accessed from within inner class, needs to be declared final
                 final int finalI = i;
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
                         progressBar.setProgress(finalI);
                     }
                 });
             }
    
             handler.post(new Runnable() {
                 @Override
                 public void run() {
                     textMsg.setText("finished");
                 }
             });
         }
     };
    
     myHandlerThread.start();
     myHandlerThread.prepareHandler();
    
     btnStart.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             myHandlerThread.postTask(myRunnable);
         }
     });
    

    }

    @Override
    protected void onDestroy() {
    myHandlerThread.quit();
    super.onDestroy();
    }

    public class MyHandlerThread extends HandlerThread {

     private Handler handler;
    
     public MyHandlerThread(String name) {
         super(name);
     }
    
     public void postTask(Runnable task){
         handler.post(task);
     }
    
     public void prepareHandler(){
         handler = new Handler(getLooper());
     }
    

    }
    }

此外弟翘,還需在清單文件中添加權(quán)限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

實現(xiàn)效果圖:


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骄酗,隨后出現(xiàn)的幾起案子稀余,更是在濱河造成了極大的恐慌,老刑警劉巖趋翻,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睛琳,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機师骗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門历等,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辟癌,你說我怎么就攤上這事寒屯。” “怎么了黍少?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵寡夹,是天一觀的道長。 經(jīng)常有香客問我厂置,道長菩掏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任昵济,我火速辦了婚禮智绸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砸紊。我一直安慰自己传于,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布醉顽。 她就那樣靜靜地躺著沼溜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪游添。 梳的紋絲不亂的頭發(fā)上系草,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音唆涝,去河邊找鬼找都。 笑死,一個胖子當著我的面吹牛廊酣,可吹牛的內(nèi)容都是我干的能耻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼亡驰,長吁一口氣:“原來是場噩夢啊……” “哼晓猛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起凡辱,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤戒职,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后透乾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洪燥,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡磕秤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捧韵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片市咆。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖再来,靈堂內(nèi)的尸體忽然破棺而出床绪,到底是詐尸還是另有隱情,我是刑警寧澤其弊,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站膀斋,受9級特大地震影響梭伐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仰担,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一糊识、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摔蓝,春花似錦赂苗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至猜谚,卻和暖如春败砂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背魏铅。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工昌犹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人览芳。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓斜姥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沧竟。 傳聞我的和親對象是個殘疾皇子铸敏,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

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