天天看點

探索Handler機制原理

衆所周知,Android是不能在主線程進行耗時操作,否則會抛出ANR(應用程式無響應)異常。于是,我們在程式開發中就會廣泛地使用Handler來實作異步消息的處理,如讀取網絡資料并加載到程式的UI界面中。Handler的運作需要MessegeQueue和Looper的支撐,關于其三者的關系,接下來将通過源碼來具體分析。

首先,我們來看一個使用Handler的情況:

public class MainActivity extends Activity {
    private Handler handler1, handler2;

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

        handler1 = new Handler();  //第一個Handler對象在主線程建立

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler2 = new Handler(); //第二個Handler在子線程建立
            }
        }).start();
    }
}
           

結果:

探索Handler機制原理

我們可以看到,出現了一個異常:Can’t create handler inside thread that has not called Looper.prepare()。該異常意思是不能在沒有調用 Looper.prepare()的線程中建立Handler。試着在handler2 = new Handler()前添加一句Looper.prepare(),再次運作,會發現錯誤消失了!

既然,異常來自于Handler,我們可以看一下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) == ) {
                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()");//異常在此處抛出,mLooper為null
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
           

明顯,mLooper為null,它是通過mLooper = Looper.myLooper()該語句來指派的,那麼我們有必要看看Looper中的myLooper()這個方法:

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}
           

代碼比較簡單,可以看出,mLooper是從ThreadLocal中取出來的(每個線程都保留獨立的變量,關于ThreadLocal,可以參考一下http://blog.csdn.net/lufeng20/article/details/24314381)。那麼sThreadLocal裡的Looper對象在什麼時候添加進去了呢?我們可以看看上面說到的Looper的prepare()方法:

public static void prepare() { 
        prepare(true);//調用下面重載的prepare()方法
}

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)); //為sThreadLocal添加一個Looper對象
}
           

看到這裡,我們或許可以知道為什麼文章開頭那個代碼片段會出錯了。如果我們不使用Looper.prepare()方法的話,就無法為sThreadLocal添加Looper對象,進而在建立Handler對象時mLooper = null,抛出異常。進一步想,為什麼handler1就不用調用Looper.prepare()這個方法呢?原因在于ActivityThread中就已經調用了Looper.prepareMainLooper()這個方法,進而調用了Looper.prepare()方法。具體可以閱讀ActivityThread中的main函數源碼可知。

那麼,我們繼續探索第二種情況,也是我們常見的情況:

final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

new Thread(new Runnable() {
       @Override
        public void run() {
            Message message = new Message();
            Bundle b = new Bundle();
            b.putString("test", "test");
            message.setData(b);
            handler.sendMessage(message); //發送message
        }
}).start();
           

通過上述方式發送資料是再常見不過了。可是我們想想,既然發送了資料,那麼資料被發送到哪裡去了?那麼,我們要再深入地看看sendMessage()方法是怎麼實作的:

//最終會調用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;
        }
        //隊列不為空,調用enqueueMessage方法
        return enqueueMessage(queue, msg, uptimeMillis);
    }
           

從上面的代碼可以看到,最後調用了enqueueMessage方法,即消息入隊的意思,表明我們發送的消息是存儲在消息隊列MessageQueue 的對象中。至于上述代碼中的mQueue,則在建立Looper對象時,一并建立了:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed); //建立消息隊列,一個Looper對應一個消息隊列
        mThread = Thread.currentThread();
}
           

緊接着,我們看一下enqueueMessage方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;   //代表目前Handler對象
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis); //調用隊列中的enqueueMessage方法
}
           

那麼,再看看MessageQueue中的enqueueMessage方法:

boolean enqueueMessage(Message msg, long when) {
        //代碼較長,使用簡單的僞代碼表示

        synchronized (this) {
            Message p = mMessages;  //設定目前消息

            //入隊操作
            msg.next = p; // invariant: p == prev.next
            prev.next = msg; //指向下一消息
            }
        return true;
}
           

到這裡,我們已經明白了發送的消息是如何處理的,那麼既然入隊了,肯定要知道它是如何處理隊列裡的消息。我們可以找到Looper中的loop()方法來解釋:

//同樣隻是摘取重要的代碼
 public static void loop() {
        final Looper me = myLooper(); //擷取Handler對應的Looper對象
        final MessageQueue queue = me.mQueue; //擷取目前消息隊列

        for (;;) {               //不斷循壞的處理消息
            Message msg = queue.next(); // 擷取消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg); //msg.target為Handler對象,回調dispatchMessage方法
            msg.recycleUnchecked();
        }
    }
           

那麼Handler的最終實作方式應該就在dispatchMessage方法裡了:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {  //如果設定了回調函數,則調用設定的回調函數
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg); //如果沒有設定回調函數,則預設調用handleMessage方法
        }
    }
           

設定了回調函數的情況為:

final Handler handler = new Handler(new Handler.Callback() { //設定回調函數
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
});
           

關于設定回調函數進而調用handleMessage方法和不設定來調用的差別,個人了解在于優先級,也類似于線程中實作Runable接口和繼承Thread類。

好了,到這裡,關于Handler,MessegeQueue和Looper三者之間的關系基本理清了,我們可以用這種标準的形式來實作并闡述Handler機制:

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                    }
                };
                Looper.loop();
            }
        }).start();
           

最後畫個示意圖來總結一下:

探索Handler機制原理