Android在子線創建Handler出現異常的原因及解決辦法

在日常的代碼編寫中,Handler主要是用來進行線程間通信的一種手段,或者說一種工具來使用,一般我們都會將handler寫在主線程中,然後開啓一個Thread,在裏面進行post或者sendMessage,將Message從MessageQueue中送給Handler,然後我們獲取數據進行UI更新。

但是這是爲什麼呢?其實每一個Handler都會有一個MessageQueue,而MessageQueue是被封裝在Looper中的,而Looper又會關聯一個線程,最後就相當於一個MessageQueue關聯了線程,一個線程只有一個MessageQueue,一個Looper,之後在通過Message來打到線程間通信。

默認情況下,只有一個MessageQueue就是主線程會通過Looper.java中的prepareMainLooper()方法來創建出來,也就是Looper的生成時,會自動產生一個MessageQueue。

但是有一點要注意子線程默認情況下是沒有Looper的更不會有MessageQueue;

那麼Looper是怎麼創建的呢,Looper在主線程中,是通過prepareMainLooper()這個方法中的prepare()創建的源碼如下:

/**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {     //如果looper的線程不唯一,拋出異常,looper已經在該線程被創建了
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed)); //否則,創建一個looper 並且綁定到創建Looper的線程
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */

創建了MessageQueue之後,Looper就要綁定到創建Looper的線程,同時進行死循環,不斷傳遞MessageQueue中的Message給Handler,讓handler處理。

下面我們看一段部分源碼 這段源碼的主要功能就是死循環不斷傳遞MessageQueue中的Message給Handler 

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next();     //MessageQueue是一個隊列結構 不斷地調用.next傳遞Message
            if (msg == null) { //這裏可以看出Message是可以爲空的。
                // No message indicates that the message queue is quitting.
                return;
            }

通常我們會認爲子線程是不能創建handler的,但是這是爲什麼呢,我們先開啓一個子線程,在裏面創建一個handler看看會發生什麼吧。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(){
            Handler handler = null;
            @Override
            public void run() {
                handler = new Handler(); 
            }
        }.start();

    }
}

我們的程序崩潰了,同時拋出了Can't create handler inside thread that has not called Looper.prepare()的異常,這個異常的意思爲,我們不能在子線程創建hadnler,因爲無法調用Looper.prepare()方法,


所以我們無法使用Handler的原因就是Looper.prepare()是用來創建Looper的,因爲子線程默認不會產生Looper,所以也沒辦法產生MessageQueue,也不能發送Message來進行通信了,

解決辦法很簡單,只要我們手動執行Looper的prepare方法創建一個Looper,在執行prepare的時候,Looper會自動創建一個MessageQueue,並且綁定到Looper所在的線程,

然後通過調用loop方法,開啓無限循環就可以正常的在子線程執行Handler了代碼如下:


public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(){
            Handler handler = null;
            @Override
            public void run() {
                Looper.prepare(); //創建一個Looper,同時創建了MessageQueue,並且綁定了線程
                handler = new Handler();
                Looper.loop();//開啓Looper的循環

            }
        }.start();

    }



}

總結:

1.子線程是可以創建Handler的,不能創建的原因是因爲子線程默認不會創建Looper,當Looper爲空就會拋出上述異常,導致程序崩潰掉。

2.Looper.prepare()方法會創建一個Looper對象,並且創建的同時通過

sThreadLocal.set(new Looper(quitAllowed)); 

這行代碼來爲創建的Looper綁定線程,同時Looper創建的同時會產生一個MessageQueue。

3.Looper.loop()方法會開啓死循環,不斷地遍歷MessageQueue。

4.解決辦法子線程不能創建Handler的方法 手動調用Looper.prepare()方法創建一個Looper並且調用Looper.loop()開啓循環就可以了。

發佈了30 篇原創文章 · 獲贊 17 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章