Android中檢測(cè)當(dāng)前是否為主線程最可靠的解決方法
如果在Android中判斷某個(gè)線程是否是主線程?對(duì)于這個(gè)問(wèn)題,你可能說(shuō)根據(jù)線程的名字,當(dāng)然這個(gè)可以解決問(wèn)題,但是這樣是最可靠的么?萬(wàn)一某天Google一下子將線程的名字改稱其他神馬東西呢。
方法揭曉
下面的方法是最可靠的解決方案。
public static boolean isInMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
實(shí)際上,寫(xiě)到這里就基本解決了文章標(biāo)題的問(wèn)題了,但是僅僅研究到這里太膚淺了,刨的不夠深,所以需要繼續(xù),希望你也可以繼續(xù)讀下去。
刨根問(wèn)底
實(shí)驗(yàn)一
好,現(xiàn)在,我們對(duì)這個(gè)穩(wěn)定的方法做一些測(cè)試,首先,下面的方法會(huì)增加一些調(diào)試打印信息。
private boolean isInMainThread() {
Looper myLooper = Looper.myLooper();
Looper mainLooper = Looper.getMainLooper();
Log.i(LOGTAG, "isInMainThread myLooper=" + myLooper
+ ";mainLooper=" + mainLooper);
return myLooper == mainLooper;
}
好,然后我們?cè)谥骶€程中運(yùn)行一個(gè)測(cè)試,調(diào)用上述方法。比如我們這樣調(diào)用。
Log.i(LOGTAG, "testInMainThread inMainThread=" + isInMainThread());
OK,我們看一下輸出日志。驗(yàn)證OK。
I/TestInMainThread(32028): isInMainThread myLooper=Looper{40d35ef8};mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028): testInMainThread inMainThread=true
實(shí)驗(yàn)二
現(xiàn)在我們繼續(xù)在一個(gè)沒(méi)有消息循環(huán)的非主線程,進(jìn)行驗(yàn)證。
new Thread() {
@Override
public void run() {
Log.i(LOGTAG, "testIn NOT in MainThread isMainThread="
+ isInMainThread());
super.run();
}
}.start();
正如我們看到的如下日志結(jié)果,主線程的Looper(翻譯成循環(huán)泵,不是很好聽(tīng))已經(jīng)被初始化賦值。但是我們新創(chuàng)建的線程的looper還是null。這是因?yàn)锳ndroid中的線程默認(rèn)沒(méi)有一個(gè)和它綁定了的消息循環(huán)(Threads by default do not have a message loop associated with them. Of course, the method works)
I/TestInMainThread(32028): isInMainThread myLooper=null;mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028): testIn NOT in MainThread isMainThread=false
實(shí)驗(yàn)三
繼續(xù),我們創(chuàng)建一個(gè)綁定了消息循環(huán)的線程,根據(jù)Android開(kāi)發(fā)者文檔說(shuō)明,以下是一個(gè)典型的創(chuàng)建消息循環(huán)線程的示例,使用單獨(dú)prepare()方法和loop()方法來(lái)創(chuàng)建一個(gè)綁定到Looper的Handler。
new Thread() {
private Handler mHandler;
@Override
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Log.i(LOGTAG, "testInNonMainLooperThread isMainThread="
+ isInMainThread());
Looper.loop();
}
}.start();
OK,現(xiàn)在再次檢查以下日志,
I/TestInMainThread(32028): isInMainThread myLooper=Looper{40d72c58};mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028): testInNonMainLooperThread isMainThread=false
兩個(gè)Looper都被初始化賦值了,但是他們是不同的對(duì)象。
原理發(fā)掘
但是,這是為什么呢,這里面有什么奧秘呢? 好,讓我們看以下Looper.class
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
/**
* 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();
}
}
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));
}
/**
* Return the Looper object associated with the current thread.
* Returns null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
/** Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
對(duì)于主線程來(lái)說(shuō),prepareMainLooper這個(gè)方法會(huì)被Android運(yùn)行環(huán)境調(diào)用,而不是程序顯式調(diào)用。通過(guò)這個(gè)方法,主線程的looper被創(chuàng)建,并且將對(duì)象引用傳遞給sMainLooper。所以保證了主線程myLooper()獲取到的引用和getMainLooper()獲取到的都是同一個(gè)引用。
對(duì)于沒(méi)有消息循環(huán)的非主線程,默認(rèn)的當(dāng)前線程的looper是null,因?yàn)槟銖膩?lái)沒(méi)有手動(dòng)地調(diào)用prepare(),所以它和主線程的looper不一樣。
對(duì)于綁定了消息循環(huán)的非主線程,當(dāng)調(diào)用Looper.prepare方法時(shí),主線程的Looper已經(jīng)由Android運(yùn)行環(huán)境創(chuàng)建,當(dāng)調(diào)用prepare方法后,綁定到這個(gè)非主線程的looper被創(chuàng)建,當(dāng)然,這不可能和主線程的Looper一樣。
綜上所述,這個(gè)方法是可靠的。
相關(guān)文章
Android 中 SwipeLayout一個(gè)展示條目底層菜單的側(cè)滑控件源碼解析
這篇文章主要介紹了Android 中 SwipeLayout一個(gè)展示條目底層菜單的側(cè)滑控件源碼解析,需要的朋友可以參考下2016-12-12Android JNI c/c++調(diào)用java的實(shí)例
這篇文章主要介紹了Android JNI c/c++調(diào)用java的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-07-07Android 全局Dialog的簡(jiǎn)單實(shí)現(xiàn)方法
本篇文章主要介紹了Android 全局Dialog的簡(jiǎn)單實(shí)現(xiàn)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02Android多功能時(shí)鐘開(kāi)發(fā)案例(實(shí)戰(zhàn)篇)
這篇文章主要為大家詳細(xì)介紹了Android多功能時(shí)鐘開(kāi)發(fā)案例,開(kāi)發(fā)了時(shí)鐘、鬧鐘、計(jì)時(shí)器和秒表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05Android實(shí)現(xiàn)一個(gè)完美的倒計(jì)時(shí)功能
在Adroid應(yīng)用中,倒計(jì)時(shí)的功能使用的很多,例如點(diǎn)擊獲取短信驗(yàn)證碼之后的倒計(jì)時(shí)等等,這篇文章主要給大家介紹了關(guān)于利用Android如何實(shí)現(xiàn)一個(gè)完美的倒計(jì)時(shí)功能的相關(guān)資料,需要的朋友可以參考下2021-11-11Android實(shí)現(xiàn)旋轉(zhuǎn),放大,縮小圖片的方法
這篇文章主要介紹了Android實(shí)現(xiàn)旋轉(zhuǎn),放大,縮小圖片的方法,結(jié)合實(shí)例形式分析了Android基于Drawable針對(duì)圖片的縮放與旋轉(zhuǎn)等處理技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10