標籤:
1.先看一下最簡單的進度條樣本
EG:
package com.sxz.android.thread;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
public class HandlerDemo extends Activity {
ProgressBar bar;
AtomicBoolean isRunning = new AtomicBoolean(false);
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
bar.incrementProgressBy(5);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_progressbar);
bar = (ProgressBar) findViewById(R.id.progressBar1);
}
@Override
protected void onStart() {
super.onStart();
Thread myThread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 20 && isRunning.get(); i++) {
Message msg = mHandler.obtainMessage();
Thread.sleep(500);
msg.sendToTarget();
}
} catch (Throwable t)
{
}
}
});
isRunning.set(true);
myThread.start();
}
@Override
protected void onStop() {
super.onStop();
isRunning.set(false);
}
}
2. 基本概念
MessageQueue 訊息佇列,存放訊息的地方,按照FIFO規則執行,每一個線程只可以擁有一個MessageQueue,在建立 Looper 對象會建立一個MessageQueue對象。而MessageQueue
都會有一個對應的 Handler,Handler會向 MessageQueue通過兩種方法發送訊息.
① sendMessage. 通過 sendMessage發送的是一個 message 對象,會被 Handler的 handleMessage() 函數處理。
② post 通過 post 方法發送的是一個 runnable 對象,則會自己執行。
這兩種訊息都會插在 MessageQueue 隊尾並按先進先出的方式執行.但是通過這兩種方法發送出去的訊息執行的方式略有不同.
Message 訊息對象,MessageQueue中存放的對象。一個 MessageQueue中可以包含多個Message 對象。可以通過 Message.obtain() 或者 Handler.obtainMessage() 擷取 Message 對象.但是這並不一定是直接建立一個新的執行個體.而是先從訊息池中看有沒有可用的 Message執行個體。存在則直接取出後返回這個執行個體。如果訊息池中沒有可用的 Message 執行個體,則用給定的參數建立一個Message 對象。調用 removeMessage()時,將 Message 從 MessageQueue 中刪除,同時放入到訊息池中。
訊息,其實可以理解為線程間交流的資訊,處理資料後台線程需要更新 UI,則發送Message內含一些資料給 UI線程.
Looper 操作 MessageQueue,一個 Looper 對應一個 MessageQueue.通過調用 Looper.myLooper() 可以擷取當前線程的 Looper對象.Looper 從 MessageQueue中取出 Message然後, 交由Handler的 handleMessage() 進行處理。處理完成後,調用 Message.recycle()將其放入訊息池中.
Looper 是每條線程裡的 MessageQueue的管家。Android沒有 Gloabal 的 MessageQueue,而 Android 會自動替主線程(UI線程) 建立MessageQueue,但在子線程裡並沒有建立MessageQueue。所以調用 Looper.getMainLooper() 得到的主線程Looper不為 NULL,但是調用Looper.myLooper()得到的Looper就有可能為 NULL。
總結:幾者之間的關係:
Handler 訊息的處理者。
handler 負責將需要傳遞的資訊封裝成 Message 對象,
然後調用 sendMessage() 方法將訊息放入 MessageQueue中,
當MessageQueue迴圈到該 Message,
調用相應的handler 對象的 handleMessage() 方法對其進行處理。
Handler 都可以共用同一個 Looper 和 MessageQueue.
3. handler.post(r)同一個線程的疑惑
handler.post(r);是把r加到訊息佇列,但並未開闢新線程。等到訊息被取出時才執行。
package com.lei.handlethread;import android.os.Bundle;import android.os.Handler;import android.app.Activity;import android.view.Menu;import android.widget.Button;public class MainActivity extends Activity { private Button btn = null; private Handler handler = new Handler(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handler.post(r); setContentView(R.layout.activity_main); btn = (Button)findViewById(R.id.hello);// 用來驗證setContentView()先執行的。
String s=(String) btn.getText();//
System.out.println(s); System.out.println("activity--->"+Thread.currentThread().getId()); System.out.println("Activityname--->"+Thread.currentThread().getName()); } Runnable r = new Runnable() { public void run() { System.out.println("handler--->"+Thread.currentThread().getId()); System.out.println("handlername--->"+Thread.currentThread().getName()); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; }}
運行結果:logCat先列印如下資訊。程式運行介面過10s顯示TextView文字。
解釋:
main線程從訊息泵中取出一個訊息,處理(執行相關函數),然後再取一個,處理。所以onCreate是某一訊息處理中的執行,其中post一個訊息,只是把訊息排入佇列了,還沒執行新訊息,什麼時候執行?要等前一個訊息處理完,再次從訊息泵中取訊息處理時,它才被執行。所以先是main的system.out,再是post的system.out
相比之下,sendMessage是同步執行,用handler.sendMessage,那順序就變了。
至於setContentView(R.layout.activity_main);肯定是最先執行,程式介面最先開啟了,但是介面空間要等到Activity的Resume(即互動階段)階段才會顯示。
通過擷取介面空間ID,在Log中列印空間內容就可驗證。
Android 中Thread,Handler,Loop學習