編輯:Android開發實例
如果在Android中判斷某個線程是否是主線程?對於這個問題,你可能說根據線程的名字,當然這個可以解決問題,但是這樣是最可靠的麼?萬一某天Google一下子將線程的名字改稱其他神馬東西呢。
方法揭曉
下面的方法是最可靠的解決方案。
代碼如下:
public static boolean isInMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
實際上,寫到這裡就基本解決了文章標題的問題了,但是僅僅研究到這裡太膚淺了,刨的不夠深,所以需要繼續,希望你也可以繼續讀下去。
刨根問底
實驗一
好,現在,我們對這個穩定的方法做一些測試,首先,下面的方法會增加一些調試打印信息。
代碼如下:
private boolean isInMainThread() {
Looper myLooper = Looper.myLooper();
Looper mainLooper = Looper.getMainLooper();
Log.i(LOGTAG, "isInMainThread myLooper=" + myLooper
+ ";mainLooper=" + mainLooper);
return myLooper == mainLooper;
}
好,然後我們在主線程中運行一個測試,調用上述方法。比如我們這樣調用。
代碼如下:
Log.i(LOGTAG, "testInMainThread inMainThread=" + isInMainThread());
OK,我們看一下輸出日志。驗證OK。
代碼如下:
I/TestInMainThread(32028): isInMainThread myLooper=Looper{40d35ef8};mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028): testInMainThread inMainThread=true
實驗二
現在我們繼續在一個沒有消息循環的非主線程,進行驗證。
代碼如下:
new Thread() {
@Override
public void run() {
Log.i(LOGTAG, "testIn NOT in MainThread isMainThread="
+ isInMainThread());
super.run();
}
}.start();
正如我們看到的如下日志結果,主線程的Looper(翻譯成循環泵,不是很好聽)已經被初始化賦值。但是我們新創建的線程的looper還是null。這是因為Android中的線程默認沒有一個和它綁定了的消息循環(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
實驗三
繼續,我們創建一個綁定了消息循環的線程,根據Android開發者文檔說明,以下是一個典型的創建消息循環線程的示例,使用單獨prepare()方法和loop()方法來創建一個綁定到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,現在再次檢查以下日志,
代碼如下:
I/TestInMainThread(32028): isInMainThread myLooper=Looper{40d72c58};mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028): testInNonMainLooperThread isMainThread=false
兩個Looper都被初始化賦值了,但是他們是不同的對象。
原理發掘
但是,這是為什麼呢,這裡面有什麼奧秘呢? 好,讓我們看以下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;
}
}
對於主線程來說,prepareMainLooper這個方法會被Android運行環境調用,而不是程序顯式調用。通過這個方法,主線程的looper被創建,並且將對象引用傳遞給sMainLooper。所以保證了主線程myLooper()獲取到的引用和getMainLooper()獲取到的都是同一個引用。
對於沒有消息循環的非主線程,默認的當前線程的looper是null,因為你從來沒有手動地調用prepare(),所以它和主線程的looper不一樣。
對於綁定了消息循環的非主線程,當調用Looper.prepare方法時,主線程的Looper已經由Android運行環境創建,當調用prepare方法後,綁定到這個非主線程的looper被創建,當然,這不可能和主線程的Looper一樣。
綜上所述,這個方法是可靠的。
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
step1:新建一個項目Compass,並將一張指南針圖片導入到res/drawable-hdpi目錄中 step2:設計應用的UI界面,main.x
谷歌官方提供了apktool可以逆向已經發布出去的APK應用,即反編譯已經打包成功的APK文件,使用它可以將其反編譯成非常接近打包前的原始格式,對於APK來說,可
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用