1. 程式人生 > >Android訊息機制---Handler工作原理

Android訊息機制---Handler工作原理

簡述:

    子執行緒沒有辦法對UI介面上的內容進行操作,如果操作,將丟擲異常:CalledFromWrongThreadException為了實現子執行緒中操作UI介面,Android中引入了Handler訊息傳遞機制。
    什麼是Handler?
    handler通俗一點講就是用來在各個執行緒之間傳送資料的處理物件。在任何執行緒中,只要獲得了另一個執行緒的handler,則可以通過  handler.sendMessage(message)方法向那個執行緒傳送資料。基於這個機制,我們在處理多執行緒的時候可以新建一個thread,這個thread擁有UI執行緒中的一個handler。當thread處理完一些耗時的操作後通過傳遞過來的handler向UI執行緒傳送資料,由UI執行緒去更新介面。 

    handler可以分發Message物件和Runnable物件到主執行緒中, 每個Handler例項,都會繫結到建立他的執行緒中(一般是位於主執行緒),它有兩個作用:

        (1)安排Message或Runnable 在某個主執行緒中某個地方執行;

        (2)安排一個動作在不同的執行緒中執行。

handler執行緒間通訊示意圖

  

基本使用:

    1、子執行緒向主執行緒傳送訊息Message

          Handler的一種經典的使用方法,也是更新UI最常用的方法。

                                                 


程式碼:

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

public class MainActivity extends AppCompatActivity {
    private Button button;
    private TextView txt;
    private static final int WORKTHREAD = 1;

    private Handler mainThreadHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == WORKTHREAD) {
                final String data = (String) msg.obj;
                txt.setText(data);
                Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        }
    };


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

        button = (Button) findViewById(R.id.btn);
        txt = (TextView) findViewById(R.id.txt);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //開啟一個工作執行緒
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = Message.obtain();
                        message.what = WORKTHREAD;
                        message.obj = "來自子執行緒資料!";
                        mainThreadHandler.sendMessage(message);
                    }
                }).start();

            }
        });
    }
}

2、主執行緒向子執行緒傳送訊息

      在平時的開發中有時會碰到主執行緒子執行緒傳送訊息,這時候來考慮這個問題。

                                             

程式碼:

public class MainActivity extends AppCompatActivity {
    private TextView txt;
    private Button btn_main_start;
    Handler workHandler;
   
    private static final int MAIN_THREAD = 2;


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

        intiViews();

        //開啟子執行緒接收主執行緒資料
        final WorkThread workThread = new WorkThread();
        workThread.start();


        btn_main_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //主執行緒傳送資料
                Message message = Message.obtain();
                message.what = MAIN_THREAD;
                message.obj = "來自主執行緒資料!";
                workHandler.sendMessage(message);
            }
        });


    }

    private void intiViews() {
        txt = (TextView) findViewById(R.id.txt);
        btn_main_start = (Button) findViewById(R.id.btn_main_start);
    }


    class WorkThread extends Thread {
        public void run() {
            Looper.prepare();
            workHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == MAIN_THREAD) {
                        final String data = (String) msg.obj;
                        txt.setText(data);
                        Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
                    }
                }
            };
            Looper.loop();
        }
    }

}

從上面的程式碼中=可能會有一些疑問,比如:

    (1)為什麼主執行緒向子執行緒傳送訊息時要呼叫手動下列程式碼:  

            Looper.prepare();
            workHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                   //do something
                } 
            };
            Looper.loop();

       (2)為什麼new Handler()構造方法中沒有使用當前執行緒的Looper而是使用了主執行緒Looper?

           如果子執行緒涉及到更新UI的操作需要使用主執行緒的Looper,如果沒有涉及到跟新UI的操作而只是簡單的處理訊息可以使用當前執行緒的Looper.

   為了對Handler作進一步的瞭解,接下來我們分析一下原始碼。

原始碼分析:

先來看一下Handler的構造方法:

Public constructors

Default constructor associates this handler with the  for the current thread.

Constructor associates this handler with the  for the current thread and takes a callback interface in which you can handle messages.

Use the provided  instead of the default one.

Use the provided  instead of the default one and take a callback interface in which to handle messages.

    我們開啟原始碼就會發現Handler(),Handler(Handler.Callbackcallback)以及Handler(boolean async)最終都會呼叫下面這個構造方法:
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        //如果Handler為匿名內部類、區域性內部類或者非靜態成員內部類(即除了靜態內部類外的所有內部類)立即警告“潛在的記憶體洩漏”
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    //沒有指定Looper,預設使用當前執行緒的Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
  這個構造方法中做了兩件事: ①檢測“潛在的記憶體洩漏”(與該博文無太多關係,如果想了解更多請看:Handler引發記憶體洩漏)②關聯當前執行緒的Looper.

   再看其他兩個構造方法:Handler(Looper looper)和Handler(Looper looper, Callback callback)。這兩個構造方法最終呼叫的是下面的構造方法:

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

從以上兩段程式碼可以看出:Handler構造方法需要先關聯一個Looper,

接下來再來探一探sendMessage(Message msg)方法:

public final boolean sendMessage(Message msg)              
{                                                          
    return sendMessageDelayed(msg, 0);                     
}                                                          

該方法最後呼叫了sendMessageDelayed()方法,我們找到該方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis)      
{                                                                           
    if (delayMillis < 0) {                                                  
        delayMillis = 0;                                                    
    }                                                                       
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}                                                                           

該方法呼叫了sendMessageAtTime()方法,我們繼續追蹤下去:
 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {   
     MessageQueue queue = mQueue;                                     
     if (queue == null) {                                             
         RuntimeException e = new RuntimeException(                   
                 this + " sendMessageAtTime() called with no mQueue");
         Log.w("Looper", e.getMessage(), e);                          
         return false;                                                
     }                                                                
     return enqueueMessage(queue, msg, uptimeMillis);                 
 }                                                                    
     到這裡就算是結束了,它首先獲取當前Looper所關聯的MessageQueue,然後將Message儲存到相應的MessageQueue()中。關於enqueueMessaage()方法參見前篇部落格:MessageQueue工作原理

Handler傳送訊息已經清楚了,接下來再來看看Handler如何處理訊息。

在使用Handler的時候我們每次都要呼叫(主執行緒除外)Looper.loop()方法,這個方法作用:當MQ中有訊息要執行時呼叫該條Message所屬Handler的dispatchMessage(msg)方法處理訊息(詳細見:Looper工作原理),ok,我們開啟dispatchMessage(msg)原始碼:

public void dispatchMessage(Message msg) {          
    if (msg.callback != null) {                     
        handleCallback(msg);                        
    } else {                                        
        if (mCallback != null) {                    
            if (mCallback.handleMessage(msg)) {     
                return;                             
            }                                       
        }                                           
        handleMessage(msg);                         
    }                                               
}                                                   
                                                    
   該方法中首先判斷Message是否存在其介面CallBack,如果有就呼叫handleCallback(msg),否則判斷時候Handler介面Callback是否為null,如果不為null就呼叫CallBack.handleMessage(msg)方法,否則呼叫Handler的handleMessage(msg)方法。而CallBack.handleMessage(msg)方法和Handler的handleMessage(msg)方法都需要開發者自己手動處理。