天天看點

Android Handler源碼淺析

  • 前言

Android開發的小夥伴對于Handler一定不陌生了,基本面試必問的東西,但是很多人都是死記硬背不了解原理,這樣面試很容易就丢分了,是以本文将會簡單帶大家了解一下Hander源碼的實作。因為是淺析,是以有些東西不會講的太細,但是會帶大家把源碼走通。

  • 上代碼

public class HandlerActivity extends AppCompatActivity {

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("lkx", (String) msg.obj);
        }
    };

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

    /**
     * 點選發送消息
     */
    public void onClick(View view) {
        new Thread(() -> {
            Message message = Message.obtain();
            message.obj = "好好學習";
            mHandler.sendMessage(message);
        }).start();
    }
}
           

這應該是大家平時寫到吐的代碼了吧,主要是從子線程發送一條消息到主線程的過程。

  • 消息進隊列 (源碼解析)

建立一個message并指派obj參數,然後攜帶message調用了sendMessage方法

new Thread(() -> {
    Message message = Message.obtain();
    message.obj = "好好學習";
    mHandler.sendMessage(message);
}).start();
           
  • **Handler#sendMessage**

sendMessage調用了sendMessageDelayed

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}
           
  • **Handler#sendMessageDelayed**

sendMessageDelayed調用了sendMessageAtTime

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
           
  • **Handler#sendMessageAtTime**

sendMessageAtTime調用了enqueueMessage,這裡出現了mQueue,也就是消息隊列,暫時不用管,下面會解釋。

public boolean sendMessageAtTime(@NonNull 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;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
           
  • **Handler#enqueueMessage**

enqueueMessage調用了queue.enqueueMessage(),并把message傳了進去

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
           
  • 消息進隊列 (總結)

上面流程走完,大家是不是很懵逼,mQueue消息隊列是啥玩意?

mQueue初次相遇

應該是sendMessageAtTime方法:

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    ...
    return enqueueMessage(queue, msg, uptimeMillis);
}
           

看起來是個成員變量,我們去成員變量找一下:

public class Handler {
	...
	final MessageQueue mQueue;
	...
}
           

找找在哪裡指派的:

public Handler(@Nullable Callback callback, boolean async) {
	...
	mLooper = Looper.myLooper();
	...
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
           

原來是在兩個參數的構造方法中被指派了,調用Looper.myLooper() 拿到mLooper,然後調用mLooper.mQueue方法,也就是說mQueue是Looper的一個成員變量。

以我們目前分析的源碼,隻知道我們把Message放入了MessageQueue中:

Android Handler源碼淺析
  • Looper源碼解析

Looper初次相遇

是在Handler兩個參數的構造方法中,但是Looper并不是在這裡建立的,那在哪裡建立的呢?一切還要從盤古開天地說起:

在我們的程式啟動後,經過一系列的啟動流程,最終将會調用我們的ActivityThread.java這個類,這個類裡面有個main()方法,這個main方法大家一定不會陌生:

  • **ActivityThread#main**

ActivityThread的main()首先調用了Looper.prepareMainLooper,初始化了Looper

public static void main(String[] args) {
	...
     Looper.prepareMainLooper();

	...
     Looper.loop();
 }
           
  • **Looper#prepareMainLooper**

調用了prepare并傳入了false

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
           
  • **Looper#prepare**

這裡new了一個Looper,然後放進了ThreadLocal裡面

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));
}
           
  • **Looper#Looper**

Looper構造方法中初始化了一個MessageQueue

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
           
  • ThreadLocal是什麼?

ThreadLocal源碼還是比較複雜的,是以我會專門寫一篇文章來講解,大家暫時了解為: 同一個ThreadLocal在不同的線程中set和get可以實作資料隔離,線程之間互不影響。比如A線程存入一個1,B線程存儲一個2,那在A線程中擷取的值是1,B線程擷取的值是2

public class HelloThreadLocal {
    public static void main(String[] args) {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

        new Thread(() -> {
            threadLocal.set(1);
            System.out.println(Thread.currentThread().getName()+": "+threadLocal.get());
        },"線程A").start();

        new Thread(() -> {
            threadLocal.set(2);
            System.out.println(Thread.currentThread().getName()+": "+threadLocal.get());
        },"線程B").start();
    }
}
           
執行結果:
線程A: 1
線程B: 2
           
  • **回到Looper#prepare**

現在我們學會了ThreadLocal,再來看一遍這個代碼,這裡有一個判斷,如果ThreadLocal擷取的值不為空,就會抛出異常,否則就存儲一個Looper到目前線程,這裡用了ThreadLocal的特性,可以保證我們一個線程最多隻有一個Looper。

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));
}
           
  • **回到ActivityThread#main**

上面我們已經分析完了Looper.prepareMainLooper(),會建立一個Looper對象存到ThreadLocal,現在我們看到main()還執行了Looper.loop()方法。

public static void main(String[] args) {
	...
     Looper.prepareMainLooper();

	...
     Looper.loop();
 }
           
  • **Looper#loop**

loop方法中的代碼特别多,這裡我們精簡之後大概還剩這麼多代碼:

  1. 通過myLooper()從ThreadLocal中拿到我們存放的Looper對象me
  2. 從me中拿到Looper的消息隊列queue
  3. 建立一個死循環不停地從queue消息隊列中取資料
  4. 如果資料為null就直接return,如果有資料就調用Handler的dispatchMessage方法
public static void loop() {
    final Looper me = myLooper();
	...
    final MessageQueue queue = me.mQueue;
	...
    for (;;) {
        Message msg = queue.next();
        if (msg == null) {
            return;
        }
        ...
        msg.target.dispatchMessage(msg); //如果消息不等于null,就會走這裡
    }
    ...
}
           
  • **Handler#dispatchMessage**

如果消息隊列循環不等于null,就會走這個方法,然後調用handleMessage(msg)

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
           
  • **Handler#handleMessage**

這個方法是一個空實作,最終由使用者定義的Handler子類實作。

public void handleMessage(@NonNull Message msg) {
}
           
  • **自己的定義的Handler#handleMessage**

會回調到我們自己的Handler的handleMessage方法,并傳回Message

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.d("lkx", (String) msg.obj);
    }
};
           

大概流程好像是走通了…

  • 總結

上面已經簡單分析了Handler的核心源碼,可能很多人還是無法把這個流程串起來,是以我們簡單總結一下:

  1. 在使用者打開APP的時候就會執行ActivityThread的mian()。
  2. main()方法中會調用Looper.prepareMainLooper()建立一個Looper存入ThreadLocal中。
  3. main()方法建立完Looper後會調用Looper.loop()。
  4. loop方法會建立死循環不停地從MessageQueue中擷取消息。
  5. 如果拿到消息就會回調我們的Handler.handleMessage()方法。
  6. ooooooooooooo上面的消息循環機制已經開始執行oooooooooooooooo
  7. 現在使用者建立一個消息,并調用Handler.sendMessage(message)。
  8. 最終會調用到queue.enqueueMessage(),将Message方法MessageQueue中。
  9. 如果消息隊列有消息了,就會執行上面的第5步,這裡就形成了一個完整的閉環。
Android Handler源碼淺析
  • 常見面試題

  • 一個線程可以有幾個Handler?
一個線程可以有多個Handler。
  • 一個線程可以有幾個Looper?怎麼保證的?
一個線程隻能有一個Looper,線上程建立的時候會調用Looper.prepare()方法進行初始化,它會建立new一個Looper對象并存到ThreadLocal中,當再次建立Looper對象的時候,會先從ThreadLocal中取出Looper,如果不為空就直接抛出異常,這樣就可以保證一個線程隻能有一個Looper了。
  • 子線程中可以使用Handler嗎?
子線程可以使用Handler,但是需要我們自己去建立Looper,是以我們需要調用Looper.prepare()進行初始化,然後再調用Looper.loop()進行循環。
  • Message如何建立更好?
我們可以直接使用new Message()進行建立,但是這樣不好,推薦使用Message.obtain()進行建立,這樣可以複用Message,減少對象的頻繁建立,避免記憶體抖動。
  • Handler會引起記憶體洩露嗎?為什麼?
Handler會引起記憶體洩露,如果發送一個延遲的Message,當延遲還沒有結束的時候銷毀了Activity,這個時候因為Message持有Handler的引用,Handler是内部類預設持有外部類的引用,是以Activity無法被回收,就會引起記憶體洩露。
  • Handler記憶體洩露怎麼解決?
可以把Handler改為靜态内部類的方式,然後使用弱引用持有Activity的引用,GC在回收對象的時候,遇到弱引用會直接回收掉,就可以避免記憶體洩露了。

最後

小編學習提升時,順帶從網上收集整理了一些 Android 開發相關的學習文檔、面試題、Android 核心筆記等等文檔,希望能幫助到大家學習提升,如有需要參考的可以直接去我 CodeChina位址:https://codechina.csdn.net/u012165769/Android-T3 通路查閱。

Android Handler源碼淺析
Android Handler源碼淺析