帶你一步一步深入Handler源碼,拿下面試官不在話下,這可能是目前最全的

安卓開發yyds 2021-09-18 03:55:10 阅读数:1,004

一步 一步 深入 handler 拿下
 }

}
};
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnTest = (Button) findViewById(R.id.btn_test);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
mTestHandler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}

在對某件實物進一步了解之前,我們要先對該事物的價值意義有一個理解,即他是做什麼的,再明白事物產生或發生時做了什麼,結束時又會有什麼樣的結果。我們要討論研究的是這個過程到底經曆了什麼,是發生什麼因,再經曆什麼產生這個果。
當調用Handler發送消息相關方法時,會把這個消息發送到哪兒去?從上面的示例代碼中可以看到消息最終還是會回到Handler手上,由他自己處理。我們要搞清楚的就是這個消息由發到收的過程。
####消息會發送到哪兒去?
>mTestHandler.sendEmptyMessage(1);
我們追隨sendEmptyMessage()方法下去:
Handler無論以何種方式發送何種消息,都會經過到sendMessageAtTime()方法:

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

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;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

而此方法會先判斷當前Handler的mQueue對象是否為空,再調用enqueueMessage()方法,從字面意思不難理解是將該消息入隊保存起來。再看enqueueMessage()方法:

  • 1.

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

public final class Message implements Parcelable {
//…
Handler target;
}

該方法會先將Message和當前Handler綁定起來,不難理解當需要處理Message時直接甩給綁定他的Handler就是了。再調用queue.enqueueMessage()方法正式入隊,而queue對象到底是一個什麼樣的對象?由單向鏈錶實現的消息隊列。queue.enqueueMessage()方法就是遍曆鏈錶將消息插入錶尾保存起來,而從queue取消息就是把錶頭的Message拿出來。
接著來搞清楚queue他是何時怎樣創建的?來看Handler的構造函數。

  • 1.
  • 2.
  • 3.

public Handler(Callback callback, boolean async) {
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.myLooper()方法看能不能獲取一個Looper對象,如果獲取不到程序就直接蹦了。再從該Looper對象中獲取我們需要的消息隊列。
Looper到底是一個怎樣的對象,有這怎樣的身份,在Handler機制中扮演這怎樣的角色?來看myLooper()方法:

  • 1.
  • 2.
  • 3.

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

myLooper()方法會直接就從sThreadLocal對象中獲取Looper,而sThreadLocal是一個ThreadLocal類對象,而ThreadLocal類說白了就是通過他存儲的對象是線程私有的。
>static final ThreadLocal sThreadLocal = new ThreadLocal();
調用get()方法直接從ThreadLocal中獲取Looper,接下來就得看是何時set()將Loooper對象保存到ThreadLocal中去的。Looper.prepare()方法:

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

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

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

從這段源碼可以看出,Looper不僅是線程私有的還是唯一不可替換。Looper對象創建時會初始化MessageQueue()對象,正是我們需要的隊列。
之所以最上面的示例代碼中我們並沒有調用prepare()方法初始化Looper,程序也沒有崩潰,那是因為在ActivityThread的Main方法中就已經初始化了Looper對象。

  • 1.
  • 2.

public final class ActivityThread {
//…
public static void main(String[] args) {
Looper.prepareMainLooper();
}
//…
}

到此我們算是明白消息會發送到哪兒去了,現在就要知道的是怎麼取出消息交給Handler處理的。
首先MessageQueue封裝有完整的添加(入隊)和獲取/删除(出隊)方法,MessageQueeue.next()方法將鏈錶當中錶頭第一個消息取出。

  • 1.
  • 2.
  • 3.

Message next() {
//…
for (; {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

 nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);

最後我還整理了很多Android中高級的PDF技術文檔。以及一些大廠面試真題解析文檔。

 CodeChina開源項目地址:《Android學習筆記總結+移動架構視頻+大廠面試真題+項目實戰源碼》

帶你一步一步深入Handler源碼,拿下面試官不在話下,這可能是目前最全的_移動開發

Android高級架構師之路很漫長,一起共勉吧!

版权声明:本文为[安卓開發yyds]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/09/20210918035509501v.html