Handler, Thread, Looper, Message,一圖勝千言

這裏寫圖片描述
問個問題:如果兩個handler共享一個Looper,那麼HandlerA發送的消息,HandlerB會不會收到囁?
如果你不知道,那就看看我這篇文章,如果知道就別看了。。。

Handler大家一定太熟悉了,就是發送Message的,然後在handler關聯的線程裏處理Message。一般用來通知消息或者切換線程工作之類的。
一個Thread對應一個Looper,Looper就是一個循環體,Looper的作用就是給Thread用來循環處理消息的。當然如果你的Thread創建不需要sendMessage什麼的,那當然就不需要創建一個Looper,比如你只是打印個字符串而已。。。
Looper既然是循環處理消息的話,那Looper中肯定有個Message的隊列,沒錯,就是MessageQueue。MessageQueue就是記錄Message隊列的,採用鏈表的方式,並且記錄列表頭部指針(Java沒有指針的概念,就是用個變量記錄第一個Message。。。)。
看下Looper使用的例子:

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();
    }
}

這是個很簡單的例子,在創建Thread的時候,先準備好Looper,只需要調用Looper.prepare()就可以了,看下prepare方法:

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

大概就是得到當前的Thread,然後new一個Looper關聯到Thread上。接下來就是生命一個Handler,看下Handler的構造函數:

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            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());
            }
        }

        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,通過Looper.myLooper得到,然後記錄Looper和Looper的MessageQueue,當然你可以調用Handler其他的構造函數來指定Looper等。
然後調用Looper.loop。loop函數中有一個死循環,是不斷的讀取MessageQueue中的Message來處理,如果隊列爲空,則該死循環會阻塞,知道有新的Message到來纔會喚起。
講了這麼多,該講講主人公Message了,Message主要有以下的參數:

public final class Message implements Parcelable { 
    //Message傳遞的一些參數
    public int what;
    public int arg1; 
    public int arg2;
    public Object obj;
    //記錄發送方
    public Messenger replyTo;

    public int sendingUid = -1;

    //記錄當前Message是不是正在被使用,因爲Message可以重複利用,所以需要有變量記錄
    /*package*/ static final int FLAG_IN_USE = 1 << 0;

    /** If set message is asynchronous */
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    /** Flags to clear in the copyFrom method */
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    /*package*/ int flags;

    //在MessageQueue中排序時是通過該值來排序
    long when;

    /*package*/ Bundle data;

    //記錄該Message是要發送給哪個Handler來處理
    Handler target;

    /*package*/ Runnable callback;

    //剛剛提到在MessageQueue中是以鏈表的方式記錄,所以next相當於指針。
    /*package*/ Message next;
}

從參數中,可以將Message分爲兩類,一種是攜帶參數信息的(what, arg1, arg2等),一種是攜帶runnable的。後者用handler執行的時候,就是直接調用runnable.run方法。對應到handler的話就是兩種發消息的方法:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

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

如果調用post的話,會將runnable封裝到Message中。
那如何創建一個Message呢?
可以通過調用Message.obtain(…)或者handler.obtainMessage(…)來調用,都是一樣的,handler.obtainMessage最後也都是調用Message裏面的方法。
在創建Message的時候,要指定Message的target是誰!!!當從MessageQueue中取出來會將該message發給message.target來處理。所以如果兩個handler共享一個Loop,通過HandlerA發送的消息只能由HandlerA來處理,HandlerB不會收到。

下面問個問題:
如何在一個子線程中彈toast?
是這樣麼?

new Thread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(TestActivity.this,"lall",Toast.LENGTH_LONG).show();
            }
        }).start();

no,no,no。當你這麼調的時候會發現出錯了。這裏要提醒下Toast中用到了handler。所以如果你直接像上面這麼寫會出錯,因爲在該子線程中Looper還沒prepare好,所以創建handler的時候會出錯。所以這就要引入一個新的Thread,就是handlerThread,該thread在創建的時候就會自己創建一個Looper並綁定,所以你可以這麼做:

        HandlerThread t = new HandlerThread("thread");
        t.start();
        Handler handler = new Handler(t.getLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(TestActivity.this,"ddd",Toast.LENGTH_LONG).show();
            }
        });
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章