重識 Handler

注:本文系統源碼展示基於 API-27(部分刪減) .

做Android 開發肯定離不開跟 Handler 打交道,Handler 作爲 Android 中消息機制的重要一員 ,它通常被我們用來做主線程與子線程之間的通信工具

可以說只要有異步線程與主線程通信的地方就一定會有 Handler

所以搞懂 Handler 對理解Android以及開發非常有必要 那麼,一起過一下Handler 用法,handler通信機制的原理,以及可能的錯誤規避

重識 Handler

我們可以使用 Handler 發送並處理與一個線程關聯的 Message 和 Runnable 。(注意:Runnable 會被封裝進一個 Message,所以它本質上還是一個 Message 

每個 Handler 都會跟一個線程綁定,並與該線程的 MessageQueue 關聯在一起,從而實現消息的管理以及線程間通信。

1.1 Handler 的基本用法

android.os.Handler handler = new Handler(){
  @Override
  public void handleMessage(final Message msg) {
    //這裏接受並處理消息
  }
};
//發送消息
handler.sendMessage(message);
handler.post(runnable);
handler.postDelayed(runnable);
實例化一個 Handler 重寫 handleMessage 方法 ,然後在需要的時候調用它的 send 以及 post 系列方法就可以了,非常簡單易用,並且支持延時消息。(更多方法可查詢 API 文檔)

當熟悉了Handler的原理之後我們知道,Handler不僅僅能將子線程的數據傳遞給主線程,它能實現任意兩個線程的數據傳遞。
子線程中創建Handler注意:1.手動調用Looper.prepare(); 2.Looper.loop();

實例化一個 Handler 重寫 handleMessage 方法 ,然後在需要的時候調用它的 send 以及 post 系列方法就可以了,非常簡單易用,並且支持延時消息。(更多方法可查詢 API 文檔)

 

當熟悉了Handler的原理之後我們知道,Handler不僅僅能將子線程的數據傳遞給主線程,它能實現任意兩個線程的數據傳遞。

子線程中創建Handler注意:1.手動調用Looper.prepare(); 2.Looper.loop();

這裏沒有看到任何 MessageQueue 的身影,也沒看到它與線程綁定的邏輯

2. Handler 原理解析

大家早就聽說過了 Looper 以及 MessageQueue 了

不過在開始分析原理之前,先明確我們的問題

  1. Handler 是如何與線程關聯的

  2. Handler 發出去的消息是誰管理的

  3. 消息又是怎麼回到 handleMessage() 方法的

  4. 線程的切換是怎麼回事

2.1 Handler 與 Looper 的關聯

實際上我們在實例化 Handler 的時候 Handler 會去檢查當前線程的 Looper 是否存在,如果不存在則會報異常,也就是說在創建 Handler 之前一定需要先創建 Looper 

代碼如下

public Handler(Callback callback, boolean async) {
            //.....
        //檢查當前的線程是否有 Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //Looper 持有一個 MessageQueue
        mQueue = mLooper.mQueue;
            //....
}
這個異常相信很多人遇到過,而我們平時直接使用感受不到這個異常是因爲主線程已經爲我們創建好了 Looper,後面會講。

 

一個完整的 Handler 使用例子其實是這樣的:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        Looper.loop();
    }
}

//在Looper.prepare()

private static void prepare(boolean quitAllowed) {
  if (sThreadLocal.get() != null) {
    throw new RuntimeException("Only one Looper may be created per thread");
  }
//該方法在當前thread創建了一個Looper(), ThreadLocal主要用於維護線程的本地變量,
  sThreadLocal.set(new Looper(quitAllowed));
}


//looper構造方法  初始化MessageQueue ,以及當前線程mThread
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper 提供了 Looper.prepare() 方法來創建 Looper ,並且會藉助 ThreadLocal 來實現與當前線程的綁定功能。Looper.loop() 則會開始不斷嘗試從 MessageQueue 中獲取 Message , 並分發給對應的 Handler(Message 的分發與處理)

也就是說 Handler 跟線程的關聯是靠 Looper 來實現的。

2.2 Message 的存儲與管理

Handler 提供了一些列的方法讓我們來發送消息,如 send()系列 post()系列 。

不過不管我們調用什麼方法,最終都會走到 Message.enqueueMessage(Message,long) 方法。

 sendEmptyMessage(int) 方法爲例:

//Handler
sendEmptyMessage(int)
-> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
        -> enqueueMessage(MessageQueue,Message,long)
            -> queue.enqueueMessage(Message, long);

查看源代碼可以看出Message被存入MessageQueue時是將Message存到了上一個Message.next上, 形成了一個鏈式的列表,同時也保證了Message列表的時序性。

查看源代碼可以看出Message被存入MessageQueue時是將Message存到了上一個Message.next上, 形成了一個鏈式的列表,同時也保證了Message列表的時序性。

到了這裏,消息的管理者 MessageQueue 也就露出了水面

MessageQueue 顧明思議就是個隊列,負責消息的入隊出隊。

Message的發送實際是放入到了Handler對應線程的MessageQueue中

這裏有兩個問題

1. sendMessageDelayed是如何實現延時發送消息的? 

2. sendMessageDelayed是通過阻塞來達到了延時發送消息的結果,那麼會不會阻塞新添加的Message?

boolean enqueueMessage(Message msg, long when) {
        ...//略過了部分拋出異常的代碼
        synchronized (this) {
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //p是當前MessageQueue隊首Message
            if (p == null || when == 0 || when < p.when) {
                //如果當前隊列沒有其他需要發送的Message;或者當前新添加進來的的Message的時間點爲0(即需要立即發送的消息);
                //或者當前新添加進來的的Message需要發送的時間點小與當前MessageQueue隊列頭部Message的時間點,
                //(即當前添加進來的Message需要在當前MessageQueue隊列頭部Message之前被髮送)時,就會進入到if代碼塊裏面。

                // New head, wake up the event queue if blocked. -->新的首部,然後如果阻塞了,需要喚醒線程。

                //此時做的事情是將當前新添加的Message插入到了MessageQueue的隊首(Message 的存儲是鏈表的形式,next相當於鏈表的尾指針)
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;

                //爲什麼會有線程的阻塞呢?其實MessageQueue內部的消息是按需要發送的時間點從小到大排列的(Handler發送消息並不是通過定時器發送的)
                //所以,當隊首Message(最近需要發送的Message)未到達發送時間點時,線程被阻塞,所以這裏需要根據線程是否阻塞看是否需要喚醒線程,這樣才能使新加入的Message能及時發送出去,不會被阻塞。
                    線程的喚醒是通過native的方法來實現的。

            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.

                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;

                //執行到了else語句裏面,說明了當前添加進來的Message是在當前MessqgeQueue隊首的Message之後纔會被髮送的
                //這裏的for循環實際操作就是找到MessageQueue中比當前添加進來的Message需要發送的時間點大的位置,將Message插入到其前邊(實際就是一個鏈表的插入操作)
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

2.3 Message 的分發與處理

瞭解清楚 Message 的發送與存儲管理後,接下來看分發與處理。

前面說到了 Looper.loop() 負責對消息的分發, Message的發送實際是放入到了Handler對應線程的MessageQueue中,那麼,Message又是如何被取出來的呢?

如果不調用Looper.loop()方法,Handler是接受不到消息的,所以我們可以大膽的猜測,消息的獲取肯定和它脫不了關係

先來看看所涉及到的方法:

//Looper
public static void loop() {
////可以看到,在調用Looper.prepare()之前是不能調用該方法的,不然又得拋出異常了
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //創建loop時初始化的mQueue -->mQueue = new MessageQueue(quitAllowed);
    final MessageQueue queue = me.mQueue;
    //...
    for (;;) {
       // 不斷從 MessageQueue 獲取 消息
        Message msg = queue.next(); // might block
        //退出 Looper
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        //...
        try {
                        //msg.target 就是發送該消息的 Handler
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            //...
        }
        //...
                         //回收 message, 見【3.5 創建 Message 實例的最佳方式】
        msg.recycleUnchecked();
    }
}

這裏我們看到,mLooper()方法裏我們取出了,當前線程的looper對象,然後從looper對象開啓了一個死循環

不斷地從looper內的MessageQueue中取出Message,只要有Message對象,就會通過Message的target調用dispatchMessage去分發消息

loop() 裏調用了 MessageQueue.next() :

//MessageQueue
Message next() {
    //...
    for (;;) {
        //...
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //...
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
        }

        // Run the idle handlers. 
        //...
    }
}

還調用了 msg.target.dispatchMessage(msg) ,msg.target 就是發送該消息的 Handler,這樣就回調到了 Handler 那邊去了:

//Handler
public void dispatchMessage(Message msg) {
  //msg.callback 是 Runnable ,如果是 post方法則會走這個 if  ; 
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    //callback 見【3.4 Handler 裏藏着的 Callback 能幹什麼】
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    //回調到 Handler 的 handleMessage 方法
    handleMessage(msg);
  }
}

注:dispatchMessage() 方法針對 Runnable 的方法做了特殊處理,如果是 post() ,則會直接執行 Runnable.run() 

分析:Looper.loop() 是個死循環,會不斷調用 MessageQueue.next() 獲取 Message ,並調用 msg.target.dispatchMessage(msg) 回到了 Handler 來分發消息,以此來完成消息的回調

注:loop()方法並不會卡死主線程,[Android中爲什麼主線程不會卡死]

那麼線程的切換又是怎麼回事呢?

其實非常簡單,我們將所涉及的方法調用棧畫出來,如下:

Thread.foo(){
      Looper.loop()
        -> MessageQueue.next()
           -> Message.target.dispatchMessage()
               -> Handler.handleMessage()
}

顯而易見,Handler.handleMessage() 所在的線程最終由調用 Looper.loop() 的線程所決定。

平時我們用的時候從異步線程發送消息到 Handler,這個 Handler 的 handleMessage() 方法是在主線程調用的,所以消息就從異步線程切換到了主線程。

圖解原理

新建一個Handler對象,通過這個對象向主線程發送信息;而我們發送的信息會先到主線程的MessageQueue進行等待,由Looper按先入先出順序取出,再根據message對象的what屬性分發給對應的Handler進行處理!

 

2.4 小結

Handler 的背後有着 Looper 以及 MessageQueue 的協助,三者通力合作,分工明確。

嘗試小結一下它們的職責,如下:

  • Looper :負責關聯線程以及消息的分發,會與創建它的線程綁定,並負責在該線程下從 MessageQueue 獲取 Message,分發給 Handler ;

  • MessageQueue :是個隊列,負責消息的存儲與管理,負責管理由 Handler 發送過來的 Message ;

  • Handler : 負責發送並處理消息,面向開發者,提供 API,並隱藏背後實現的細節。

對開頭提出的問題用一句話總結:

Handler 發送的消息由 MessageQueue 存儲管理,並由 Loopler 負責回調消息到 handleMessage()。

線程的轉換由 Looper 完成,handleMessage() 所在線程由 Looper.loop() 調用者所在線程決定。

 

3. Handler 的延伸

由於 Handler 的特性,它在 Android 裏的應用非常廣泛,比如: AsyncTask、HandlerThread、Messenger、IdleHandler 和 IntentService 等等。沒講到的可以自行搜索相關內容進行了解。

3.1 Handler 引起的內存泄露原因以及最佳解決方案

Handler 允許我們發送延時消息,如果在延時期間用戶關閉了 Activity,那麼該 Activity 會泄露。

這個泄露是因爲 Message 會持有 Handler,而又因爲 Java 的特性,內部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就導致 Activity 泄露。

解決該問題的最有效的方法是:將 Handler 定義成靜態的內部類,在內部持有 Activity 的弱引用,並及時移除所有消息

示例代碼如下:

private static class SafeHandler extends Handler {

    private WeakReference<HandlerActivity> ref;

    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}


並且再在 Activity.onDestroy() 前移除消息,加一層保障:
@Override
protected void onDestroy() {
  safeHandler.removeCallbacksAndMessages(null);
  super.onDestroy();
}

這樣雙重保障,就能完全避免內存泄露了。

注:單純的在 onDestroy 移除消息並不保險,因爲 onDestroy 並不一定執行

3.2 爲什麼我們能在主線程直接使用 Handler,而不需要創建 Looper ?

前面我們提到了每個Handler 的線程都有一個 Looper ,主線程當然也不例外,但是我們不曾準備過主線程的 Looper 而可以直接使用,這是爲何?

注意:通常我們認爲 ActivityThread 就是主線程。事實上它並不是一個線程,而是主線程操作的管理者,通常 ActivityThread 認爲就是主線程無可厚非,另外主線程也可以說成 UI 線程。

App初始化的時候 ActivityThread mMainThread 在Activity中的attach()方法中被初始化

在 ActivityThread.main() 方法中有如下代碼:

//android.app.ActivityThread
public static void main(String[] args) {
  //...
  Looper.prepareMainLooper();

  ActivityThread thread = new ActivityThread();
  thread.attach(false);

  if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
  }
  //...
  Looper.loop();

  throw new RuntimeException("Main thread loop unexpectedly exited");
}

----------------------------------------------------------
//Looper.prepareMainLooper(); 代碼如下:
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

可以看到在 ActivityThread 裏 調用了 Looper.prepareMainLooper() 方法創建了 主線程的 Looper ,並且調用了 loop() 方法,所以我們就可以直接使用 Handler 了。

注:Looper.loop() 是個死循環,後面的代碼正常情況不會執行。

3.3 主線程的 Looper 不允許退出

如果你嘗試退出 Looper ,你會得到以下錯誤信息:Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.

主線程不允許退出,退出就意味 APP 要掛。

3.4 Handler 裏藏着的 Callback 能幹什麼?

在 Handler 的構造方法中有幾個 要求傳入 Callback ,那它是什麼,又能做什麼呢?

來看看 Handler.dispatchMessage(msg) 方法:

public void dispatchMessage(Message msg) {
  //這裏的 callback 是 Runnable
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    //如果 callback 處理了該 msg 並且返回 true, 就不會再回調 handleMessage
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

可以看到 Handler.Callback 有優先處理消息的權利 ,當一條消息被 Callback 處理並攔截(返回 true),那麼 Handler 的 handleMessage(msg) 方法就不會被調用了;如果 Callback 處理了消息,但是並沒有攔截,那麼就意味着一個消息可以同時被 Callback 以及 Handler 處理

我們可以利用 Callback 這個攔截機制來攔截 Handler 的消息  ; 

場景:Hook ActivityThread.mH , 在 ActivityThread 中有個成員變量 mH ,它是個 Handler,又是個極其重要的類,幾乎所有的插件化框架都使用了這個方法。(Xposed / Legend)

(關於hook)

 

3.5 創建 Message 實例的最佳方式

由於 Handler 極爲常用,所以爲了節省開銷,Android 給 Message 設計了回收機制,所以我們在使用的時候儘量複用 Message ,減少內存消耗。

方法有二:

  1. 通過 Message 的靜態方法 Message.obtain(); 獲取;

  2. 通過 Handler 的公有方法 handler.obtainMessage(); 。

3.6 子線程裏彈 Toast/DialogDialog 的正確姿勢

當我們嘗試在子線程裏直接去彈 Toast 的時候,會 crash :

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


正確示例代碼如下:本質上是因爲 Toast 的實現依賴於 Handler,按子線程使用 Handler 的要求修改即可(見【2.1】),同理的還有 Dialog。

new Thread(new Runnable() {
  @Override
  public void run() {
    Looper.prepare();
    Toast.makeText(HandlerActivity.this, "不會崩潰啦!", Toast.LENGTH_SHORT).show();
    Looper.loop();
  }
});

3.7 妙用 Looper 機制

我們可以利用 Looper 的機制來幫助我們做一些事情:

  1. 將 Runnable post 到主線程執行;

  2. 利用 Looper 判斷當前線程是否是主線程。

完整示例代碼如下:

public final class MainThread {

    private MainThread() {
    }

    private static final Handler HANDLER = new Handler(Looper.getMainLooper());

    public static void run(@NonNull Runnable runnable) {
        if (isMainThread()) {
            runnable.run();
        }else{
            HANDLER.post(runnable);
        }
    }

    public static boolean isMainThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

}

 

4. Android中爲什麼主線程不會因爲Looper.loop()裏的死循環卡死?

(1) Android中爲什麼主線程不會因爲Looper.loop()裏的死循環卡死?

進程:每個app運行時前首先創建一個進程,該進程是由Zygote fork出來的,用於承載App上運行的各種Activity/Service等組件。進程對於上層應用來說是完全透明的, 大多數情況一個App就運行在一個進程中,除非在AndroidManifest.xml中配置Android:process屬性,或通過native代碼fork進程。

線程:線程對應用來說非常常見,比如每次new Thread().start都會創建一個新的線程。該線程與App所在進程之間資源共享,從Linux角度來說進程與線程除了是否共享資源外,並沒有本質的區別,都是一個task_struct結構體,在CPU看來進程或線程無非就是一段可執行的代碼,CPU採用CFS調度算法,保證每個task都儘可能公平的享有CPU時間片

再說說死循環問題:

對於線程既然是一段可執行的代碼,當可執行代碼執行完成後,線程生命週期便該終止了,線程退出。而對於主線程,我們是絕不希望會被運行一段時間,自己就退出,那麼如何保證能一直存活呢?簡單做法就是可執行代碼是能一直執行下去的,死循環便能保證不會被退出,例如,binder線程也是採用死循環的方法,通過循環方式不同與Binder驅動進行讀寫操作,當然並非簡單地死循環,無消息時會休眠。但這裏可能又引發了另一個問題,既然是死循環又如何去處理其他事務呢?通過創建新線程的方式。

真正會卡死主線程的操作是在回調方法onCreate/onStart/onResume等操作時間過長,會導致掉幀,甚至發生ANR,looper.loop本身不會導致應用卡死。

(2) 沒看見哪裏有相關代碼爲這個死循環準備了一個新線程去運轉?

 

事實上,會在進入死循環之前便創建了新binder線程,在代碼ActivityThread.main()中:

public static void main(String[] args) {
    //....

//創建Looper和MessageQueue對象,用於處理主線程的消息
    Looper.prepareMainLooper();


//創建ActivityThread對象
    ActivityThread thread = new ActivityThread();
//建立Binder通道 (創建新線程)
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    //....
//消息循環運行
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

thread.attach(false);便會創建一個Binder線程(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS發送來的事件),該Binder線程通過Handler將Message發送給主線程( 具體過程可查看 startService流程分析)

另外,ActivityThread實際上並非線程,不像HandlerThread類,ActivityThread並沒有真正繼承Thread類,只是往往運行在主線程,該人以線程的感覺,其實承載ActivityThread的主線程就是由Zygote fork而創建的進程。

主線程的死循環一直運行是不是特別消耗CPU資源呢? 其實不然,這裏就涉及到Linux pipe/epoll機制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裏, 此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作。 主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。

 

(3) Activity的生命週期是怎麼實現在死循環體外能夠執行起來的?

 

ActivityThread的內部類H繼承於Handler,通過handler消息機制,簡單說Handler機制用於同一個進程的線程間通信。

Activity的生命週期都是依靠主線程的Looper.loop,當收到不同Message時則採用相應措施:

在H.handleMessage(msg)方法中,根據接收到不同的msg,執行相應的生命週期。

5. 知識點彙總

由前文可得出一些知識點,彙總一下,方便記憶。

  1. Handler 的背後有 Looper、MessageQueue 支撐,Looper 負責消息分發,MessageQueue 負責消息管理;

  2. 在創建 Handler 之前一定需要先創建 Looper;

  3. Looper 有退出的功能,但是主線程的 Looper 不允許退出;

  4. 異步線程的 Looper 需要自己調用 Looper.myLooper().quit(); 退出;

  5. Runnable 被封裝進了 Message,可以說是一個特殊的 Message;

  6. Handler.handleMessage() 所在的線程是 Looper.loop() 方法被調用的線程,也可以說成 Looper 所在的線程,並不是創建 Handler 的線程;

  7. 使用內部類的方式使用 Handler 可能會導致內存泄露,即便在 Activity.onDestroy 裏移除延時消息,必須要寫成靜態內部類;


 

Android中常用線程切換:

1.開啓線程 Thread與Runnable
兩種方式均可實現多線程也都可以實現資源共享(如買票系統)
Runnable優勢: 適合多個相同的程序代碼的線程去處理同一個資源 ; 可以避免java中的單繼承的限制  ; 增加程序的健壯性

2.在子線程中更新UI
view.post(Runnable action)

3.假如該方法是在子線程中
mActivity.runOnUiThread(Runnable action)

4.Handler機制
(1)假如該方法是在子線程中
首先在主線程中定義Handler,Handler mainHandler = new Handler();(必須要在主線程中定義才能操作主線程,如果想在其他地方定義聲明時要這樣寫Handler mainHandler = new Handler(Looper.getMainLooper()),來獲取主線程的 Looper 和 Queue )
Handler mainHandler = new Handler(Looper.getMainLooper());
    mainHandler.post(new Runnable() {
        @Override
        public void run() {
            //已在主線程中,更新UI
        }
    });
(2): 假設在主線程中
Handler myHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case 0:
                    //更新UI等
} } }
5.使用AsyncTask
6.其他 :xUtils3,AsyncHttpClient,Okhttp,Volley,EventBus,rxjava等…

 


任何簡單易用的知識點背後藏着工程師大量的智慧。

相關參考:

Handler

Android消息機制1-Handler(Java 層)

Android Handler消息機制原理解讀

瞭解Android 消息機制

Android Handler攻略集合

Android多線程:手把手教你使用HandlerThread

Android 消息機制——你真的瞭解Handler?

Android Handler消息機制原理最全解讀

Handler進階之sendMessage原理探索

Android中爲什麼主線程不會卡死

一文看穿 Handler blog

Android 中的“子線程”形態解析

Android UI線程和非UI線程

Android中多線程切換的幾種方法(eventbus/rxjava)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章