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

0,寫在前面

博主在學習Handler消息傳遞機制時,學習的方法是通過問題來驅動理解,都是關注度比較高的問題,以下是我在學習后的簡單整理,歡迎吐槽!

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相關的Message和Runnable對象。 每個Handler實例都與單個線程和該線程的消息隊列相關聯(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ù)據的Message對象到隊列中,等待更新。

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

image.png

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

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

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

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

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

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

5,如何理解Handler消息處理機制?

消息處理機制本質:一個線程開啟循環(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)了,在主線程中調用Looper.prepare()...Looper.loop()就會變當前線程變成Looper線程(可以先簡單理解:無限循環(huán)不退出的線程),Looper.loop()方法里面有一段死循環(huán)的代碼,所以主線程會進入while(true){...}的代碼段跳不出來,但是主線程也不能什么都不做吧?其實所有做的事情都在while(true){...}里面做了,主線程會在死循環(huán)中不斷等其他線程給它發(fā)消息(消息包括:Activity啟動,生命周期,更新UI,控件事件等),一有消息就根據消息做相應的處理,Looper的另外一部分工作就是在循環(huán)代碼中會不斷從消息隊列挨個拿出消息給主線程處理。

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

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

  6.4,Handler 
  簡單說Handler用于同一個進程的線程間通信。Looper讓主線程無限循環(huán)地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的,那么怎樣在其他的線程往主線程的隊列里放入消息呢?其實很簡單,我們知道在同一進程中線程和線程之間資源是共享的,也就是對于任何變量在任何線程都是可以訪問和修改的,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實例,就可以往主線程的MessageQueue放入消息,主線程在輪詢的時候就會在主線程處理這個消息。那么怎么拿到主線程 MessageQueue的實例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統(tǒng)一添加消息和消息的回調處理,又專門構建了Handler類,你只要在主線程構建Handler類,那么這個Handler實例就獲取主線程MessageQueue實例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時候就通過這個引用往消息隊列里插入新消息。Handler 的另外一個作用,就是能統(tǒng)一處理消息的回調。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做,這樣的設計非常的贊。具體做法就是在隊列里面的Message持有Handler的引用(哪個handler 把它放到隊列里,message就持有了這個handler的引用),然后等到主線程輪詢到這個message的時候,就來回調我們經常重寫的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,添加任務到消息隊列中
      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ù)據對象到消息隊列中
      boolean sendMessage(Message msg)
      boolean sendMessageAtFrontOfQueue(Message msg)
      boolean sendMessageAtTime(Message msg, long uptimeMillis)
      boolean sendMessageDelayed(Message msg, long delayMillis)

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

  7.3,消息的兩種處理形式
      7.3.1,任務消息
      handler.post(new Runnable() {
         @Override
         public void run() {
           //TODO : Do some operation
         }
      });
      7.3.2,數(shù)據消息
        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());
     }
    

    }
    }

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

實現(xiàn)效果圖:


image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容