1. 程式人生 > >Android面試系列之Handler機制篇

Android面試系列之Handler機制篇

1.什麼是Handler?   Handler是可以通過傳送和處理Message和Runnable物件來關聯相應執行緒的MessageQueue。通常我們認為它是一種非同步機制。

  a.可以讓對應的Message和Runnable在未來的某個時間點進行相應的處理。

  b.讓自己想要的耗時操作在子執行緒中完成,讓更新UI的操作在主執行緒中完成,而子執行緒與主執行緒之間的通訊就是靠Handler來完成。

2.Handler的使用方法   Handler提供了很多非同步機制的方法,只不過我們常用就只有post和sendMessage系列方法,我們先來看看Handler提供的構造器吧:

Handler():預設建構函式將此處理程式與Looper用於當前執行緒。 Handler(Handler.Callback callback):建構函式將此處理程式與Looper對於當前執行緒,並接受一個回撥介面,您可以在其中處理訊息。 Handler(Looper looper):使用所提供的Looper而不是預設的。 Handler(Looper looper, Handler.Callback callback):使用所提供的Looper而不是預設的,而是接受一個回撥介面來處理訊息。   接下來我們就來看看Handler提供的各種方法吧:

post(Runnable r):導致將Runnable r新增到訊息佇列中。 postAtTime(Runnable r, long uptimeMillis):使Runnable r新增到訊息佇列,並在uptimeMillis. postDelayed(Runnable r, long delayMillis):使Runnable r被新增到訊息佇列,並在指定的時間流逝後執行。 removeCallbacks(Runnable r):刪除訊息佇列中所有可執行的Runnable訊息任務。 removeMessages(int what):刪除訊息佇列中訊息物件what欄位為"what"的訊息任務。 sendEmptyMessage(int what):傳送一個空訊息物件,並設定這個空訊息的what值。 sendEmptyMessageAtTime(int what, long uptimeMillis):傳送只包含要在特定時間傳遞的值的訊息。 sendEmptyMessageDelayed(int what, long delayMillis):傳送一條訊息,該訊息只包含要在指定的時間間隔後傳遞的值。 sendMessage(Message msg):將訊息推送到訊息佇列的末尾,在當前時間之前完成所有掛起的訊息。 sendMessageAtTime(Message msg, long uptimeMillis):在所有掛起的訊息在絕對時間之前uptimeMillis(以毫秒為單位)之前,將訊息放入訊息佇列中。 sendMessageDelayed(Message msg, long delayMillis):在所有掛起的訊息之前(當前時間+delayMillis)之後,將訊息放入訊息佇列中。 3.Handler內部的實現機制   面試的時候,Handler的原理問到的概率還是蠻大的,不光只是為了面試,我們好好去了解一下Handler的原理,那麼對於我們去使用Handler而言是非常有好處的,至少我們知道我們傳送的訊息如何傳送到了UI執行緒的,當出現問題時,我們只要小小一分析便會知道問題發生於何處,然後解決它,好了,閒話不多扯了,下面來介紹Handler機制的實現原理。   Handler機制也可叫非同步訊息機制,它主要由4個部分組成:Message,Handler,MessageQueue,Looper,在上面我們已經接觸到了Message和Handler,接下來我們對4個成員進行著重的瞭解:

1.Message   Message是線上程之間傳遞的訊息,它可以在內部攜帶少量的資訊,用於在不同執行緒之間交換資料。使用Message的arg1和arg2便可攜帶int資料,使用obj便可攜帶Object型別資料。

2.Handler   Handler顧名思義就是處理者的意思,它只要用於在子執行緒傳送訊息物件Message,在UI執行緒處理訊息物件Message,在子執行緒呼叫sendMessage方法傳送訊息物件Message,而傳送的訊息經過一系列地輾轉之後最終會被傳遞到Handler的handleMessage方法中,最終在handleMessage方法中訊息物件Message被處理。

3.MessageQueue   MessageQueue就是訊息佇列的意思,它只要用於存放所有通過Handler傳送過來的訊息。這部分訊息會一直存放於訊息隊列當中,等待被處理。每個執行緒中只會有一個MessageQueue物件,請牢記這句話。其實從字面上就可以看出,MessageQueue底層資料結構是佇列,而且這個佇列只存放Message物件。

4.Looper   Looper是每個執行緒中的MessageQueue的管家,呼叫Looper的loop()方法後,就會進入到一個無限迴圈當中,然後每當MesssageQueue中存在一條訊息,Looper就會將這條訊息取出,並將它傳遞到Handler的handleMessage()方法中。每個執行緒只有一個Looper物件。

  瞭解了上述Handler機制的4個成員後,我們再來把思路理一遍:首先在UI執行緒我們建立了一個Handler例項物件,無論是匿名內部類還是自定義類生成的Handler例項物件,我們都需要對handleMessage方法進行重寫,在handleMessage方法中我們可以通過引數msg來寫接受訊息過後UIi執行緒的邏輯處理,接著我們建立子執行緒,在子執行緒中需要更新UI的時候,新建一個Message物件,並且將訊息的資料記錄在這個訊息物件Message的內部,比如arg1,arg2,obj等,然後通過前面的Handler例項物件呼叫sendMessge方法把這個Message例項物件傳送出去,之後這個訊息會被存放於MessageQueue中等待被處理,此時MessageQueue的管家Looper正在不停的把MessageQueue存在的訊息取出來,通過回撥dispatchMessage方法將訊息傳遞給Handler的handleMessage方法,最終前面提到的訊息會被Looper從MessageQueue中取出來傳遞給handleMessage方法,最終得到處理。這就是Handler機制整個的工作流程,怎麼樣?你懂了嗎?看看下面的圖你就更懂了:

4.Handler引起的記憶體洩漏以及解決方法 原因:非靜態內部類持有外部類的匿名引用,導致外部activity無法得到釋放。

解決方法:handler內部持有外部的弱引用,並把handler改為靜態內部類,在activity的onDestory()中呼叫handler的removeCallback()方法。

http://blog.csdn.net/javazejian/article/details/50839443

5.如何使用Handler讓子執行緒與子執行緒之間進行通訊?   如今的面試,Handler基本上很多面試者都會流暢的說出來,但是如果想要給面試官亮點,那就需要我們對Handler瞭解的十分透徹,我們通常程式碼中是子執行緒與主執行緒進行非同步通訊,那麼子執行緒與子執行緒之間也可以嗎?當然可以,只不過我們需要對Looper足夠了解,深入研究Hanlder你可以看筆者Handler原始碼分析部分。那麼我們如果需要子執行緒A和子執行緒B之間進行Handler通訊,而且是子執行緒A向子執行緒B傳送訊息,那麼我們應該怎樣做呢?

public class MainActivity extends AppCompatActivity {

    private Handler threadHandler;

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

    class ThreadA extends Thread{

        @Override         public void run() {             super.run();             Looper.prepare();

            threadHandler = new Handler(){

                @Override                 public void handleMessage(Message msg) {                     super.handleMessage(msg);                     //收到來自於ThreadB的訊息,注意這裡執行在ThreadA執行緒中                                      //......                 }             };

            Looper.loop();

        }     }

    class ThreadB extends Thread{

        @Override         public void run() {             super.run();

            Looper looper = Looper.myLooper();

            Message message = new Message();             message.obj = "ThreadB傳送訊息到ThreadA";             //......             threadHandler.sendMessage(message);

        }     } }   筆者寫的這份程式碼也許不好,但是沒有關係,我們只需要學會其中幾個關鍵的地方,如何讓ThreadB往ThreadA中傳送訊息,首先你得在ThreadA中準備一個Looper,也就是訊息輪詢大管家,然後準備傳送訊息的Handler,準備傳送訊息的Handler很容易理解,那就是在ThreadA中生成一個Handler物件即可, 那麼準備Looper怎麼做呢?在ThreadA中呼叫Looper.prepare(),然後再呼叫Looper.loop()即可,為什麼要這麼呼叫呢?我們怎麼沒有在UI主執行緒看到這樣呼叫的程式碼呢?其實它們的呼叫在原始碼裡面,我們並沒有看到,因此為了非常熟悉Handler機制,我們需要去研究研究Handler的原始碼,這樣我們才會知道筆者這裡的程式碼為什麼這麼寫才能讓ThreadB執行緒往ThreadA執行緒傳送訊息從而達到子執行緒與子執行緒進行Handler非同步通訊的目的。