轉(zhuǎn)自:http://www.cnblogs.com/-OYK/archive/2011/08/03/2126657.html
Android的消息機(jī)制(一)
android 有一種叫消息隊(duì)列的說法,這里我們可以這樣理解:假如一個(gè)隧道就是一個(gè)消息隊(duì)列,那么里面的每一部汽車就是一個(gè)一個(gè)消息,這里我們先忽略掉超車等種種因素,只那么先進(jìn)隧道的車將會(huì)先出,這個(gè)機(jī)制跟我們android 的消息機(jī)制是一樣的。
一、 角色描述
1.Looper:(相當(dāng)于隧道) 一個(gè)線程可以產(chǎn)生一個(gè)Looper 對(duì)象,由它來管理此線程里的Message Queue( 車隊(duì),消息隧道) 。
2.Handler: 你可以構(gòu)造Handler 對(duì)象來與Looper 溝通,以便push 新消息到Message Queue 里;或者接收Looper( 從Message Queue 取出) 所送來的消息。
3. Message Queue( 消息隊(duì)列): 用來存放線程放入的消息。
4 .線程:UI thread 通常就是main thread ,而Android 啟動(dòng)程序時(shí)會(huì)替它建立一個(gè)Message Queue 。
每一個(gè)線程里可含有一個(gè)Looper 對(duì)象以及一個(gè)MessageQueue 數(shù)據(jù)結(jié)構(gòu)。在你的應(yīng)用程序里,可以定義Handler 的子類別來接收Looper 所送出的消息。
在你的Android 程序里,新誕生一個(gè)線程,或執(zhí)行 (Thread) 時(shí),并不會(huì)自動(dòng)建立其Message Loop 。
Android 里并沒有Global 的Message Queue 數(shù)據(jù)結(jié)構(gòu),例如,不同APK 里的對(duì)象不能透過Massage Queue 來交換訊息(Message) 。
例如:線程A 的Handler 對(duì)象可以傳遞消息給別的線程,讓別的線程B 或C 等能送消息來給線程A( 存于A 的Message Queue 里) 。
線程A 的Message Queue 里的訊息,只有線程A 所屬的對(duì)象可以處理。
使用Looper.myLooper 可以取得當(dāng)前線程的Looper 對(duì)象。
使用mHandler = new EevntHandler(Looper.myLooper()); 可用來構(gòu)造當(dāng)前線程的Handler 對(duì)象;其中,EevntHandler 是自已實(shí)現(xiàn)的Handler 的子類別。
使用mHandler = new EevntHandler(Looper.getMainLooper()); 可誕生用來處理main 線程的Handler 對(duì)象;其中,EevntHandler 是自已實(shí)現(xiàn)的Handler 的子類別。
這樣描述可能太抽像,下面舉幾個(gè)實(shí)際的例子來說明:
二、 舉例
1. 同線程內(nèi)不同組件間的消息傳遞
Looper 類用來管理特定線程內(nèi)對(duì)象之間的消息交換(Message Exchange) 。你的應(yīng)用程序可以產(chǎn)生許多個(gè)線程。而一個(gè)線程可以有許多個(gè)組件,這些組件之間常常需要互相交換訊息。如果有這種需要,您可以替線程構(gòu)造一個(gè)Looper 對(duì)象,來?yè)?dān)任訊息交換的管理工作。Looper 對(duì)象會(huì)建立一個(gè)MessageQueue 數(shù)據(jù)結(jié)構(gòu)來存放各對(duì)象傳來的消息( 包括UI 事件或System 事件等) 。如下圖:
每一個(gè)線程里可含有一個(gè)Looper 對(duì)象以及一個(gè)MessageQueue 數(shù)據(jù)結(jié)構(gòu)。在你的應(yīng)用程序里,可以定義Handler 的子類別來接收Looper 所送出的消息。
同線程不同組件之間的消息傳遞:
public class Activity1 extends Activity implements OnClickListener{
Button button = null ;
TextView text = null ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. activity1 );
button = (Button)findViewById(R.id. btn );
button .setOnClickListener( this );
text = (TextView)findViewById(R.id. content );
}
public void onClick(View v) {
switch (v.getId()) {
case R.id. btn :
Looper looper = Looper.myLooper (); // 取得當(dāng)前線程里的looper
MyHandler mHandler = new MyHandler(looper); // 構(gòu)造一個(gè)handler 使之可與looper 通信
//buton 等組件可以由mHandler 將消息傳給looper 后, 再放入messageQueue 中, 同時(shí)mHandler 也可以接受來自looper 消息
mHandler.removeMessages(0);
String msgStr = " 主線程不同組件通信: 消息來自button" ;
Message m = mHandler.obtainMessage(1, 1, 1, msgStr); // 構(gòu)造要傳遞的消息
mHandler.sendMessage(m); // 發(fā)送消息: 系統(tǒng)會(huì)自動(dòng)調(diào)用handleMessage 方法來處理消息
break ;
}
}
private class MyHandler extends Handler{
public MyHandler(Looper looper){
super (looper);
}
@Override
public void handleMessage(Message msg) { // 處理消息
text .setText(msg. obj .toString());
}
}
}
說明:
此程序啟動(dòng)時(shí),當(dāng)前線程( 即主線程, main thread) 已誕生了一個(gè)Looper 對(duì)象,并且有了一個(gè)MessageQueue 數(shù)據(jù)結(jié)構(gòu)。
looper = Looper.myLooper ();
調(diào)用Looper 類別的靜態(tài)myLooper() 函數(shù),以取得目前線程里的Looper 對(duì)象.
mHandler = new MyHandler (looper);
構(gòu)造一個(gè)MyHandler 對(duì)象來與Looper 溝通。Activity 等對(duì)象可以藉由MyHandler 對(duì)象來將消息傳給Looper ,然后放入MessageQueue 里;MyHandler 對(duì)象也扮演Listener 的角色,可接收Looper 對(duì)象所送來的消息。
Message m = mHandler.obtainMessage(1, 1, 1, obj);
先構(gòu)造一個(gè)Message 對(duì)象,并將數(shù)據(jù)存入對(duì)象里。
mHandler.sendMessage(m);
就透過mHandler 對(duì)象而將消息m 傳給Looper ,然后放入MessageQueue 里。
此時(shí),Looper 對(duì)象看到MessageQueue 里有消息m ,就將它廣播出去,mHandler 對(duì)象接到此訊息時(shí),會(huì)呼叫其handleMessage() 函數(shù)來處理,于是輸出"This my message!" 于畫面上,
Android消息處理機(jī)制(二)
角色綜述(回顧):
(1)UI thread 通常就是main thread ,而Android 啟動(dòng)程序時(shí)會(huì)替它建立一個(gè)MessageQueue 。
(2) 當(dāng)然需要一個(gè)Looper 對(duì)象,來管理該MessageQueue 。
(3) 我們可以構(gòu)造Handler 對(duì)象來push 新消息到Message Queue 里;或者接收Looper( 從Message Queue 取出) 所送來的消息。
(4) 線程A 的Handler 對(duì)象可以傳遞給別的線程,讓別的線程B 或C 等能送訊息來給線程A( 存于A 的Message Queue 里) 。
(5) 線程A 的Message Queue 里的消息,只有線程A 所屬的對(duì)象可以處理。
子線程傳遞消息給主線程
public class Activity2 extends Activity implements OnClickListener{
Button button = null ;
TextView text = null ;
MyHandler mHandler = null ;
Thread thread ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. activity1 );
button = (Button)findViewById(R.id. btn );
button .setOnClickListener( this );
text = (TextView)findViewById(R.id. content );
}
public void onClick(View v) {
switch (v.getId()) {
case R.id. btn :
thread = new MyThread();
thread .start();
break ;
}
}
private class MyHandler extends Handler{
public MyHandler(Looper looper){
super (looper);
}
@Override
public void handleMessage(Message msg) { // 處理消息
text .setText(msg. obj .toString());
}
}
private class MyThread extends Thread{
@Override
public void run() {
Looper curLooper = Looper.myLooper ();
Looper mainLooper = Looper.getMainLooper ();
String msg ;
if (curLooper== null ){
mHandler = new MyHandler(mainLooper);
msg = "curLooper is null" ;
} else {
mHandler = new MyHandler(curLooper);
msg = "This is curLooper" ;
}
mHandler .removeMessages(0);
Message m = mHandler .obtainMessage(1, 1, 1, msg);
mHandler .sendMessage(m);
}
}
}
說明:
Android 會(huì)自動(dòng)替主線程建立Message Queue 。在這個(gè)子線程里并沒有建立Message Queue 。所以,myLooper 值為null ,而mainLooper 則指向主線程里的Looper 。于是,執(zhí)行到:
mHandler = new MyHandler (mainLooper);
此mHandler 屬于主線程。
mHandler.sendMessage(m);
就將m 消息存入到主線程的Message Queue 里。mainLooper 看到Message Queue 里有訊息,就會(huì)作出處理,于是由主線程執(zhí)行到mHandler 的handleMessage() 來處理消息。
用Android線程間通信的Message機(jī)制
在Android 下面也有多線程 的概念,在C/C++中,子線程可以是一個(gè)函數(shù) ,一般都是一個(gè)帶有循環(huán)的函數(shù),來處理某些數(shù)據(jù) ,優(yōu)先線程只是一個(gè)復(fù)雜的運(yùn)算過程,所以可能不需要while循環(huán),運(yùn)算完成,函數(shù)結(jié)束,線程就銷毀。對(duì)于那些需要控制的線程,一般我們都是和互斥鎖相互關(guān)聯(lián),從而來控制線程的進(jìn)度,一般我們創(chuàng)建子線程,一種線程是很常見的,那就是帶有消息循環(huán)的線程。
消息循環(huán)是一個(gè)很有用的線程方式,曾經(jīng)自己用C在Linux下面實(shí)現(xiàn)一個(gè)消息循環(huán)的機(jī)制 ,往消息隊(duì)列里添加數(shù)據(jù),然后異步的等待消息的返回。當(dāng)消息隊(duì)列為空的時(shí)候就會(huì)掛起線程,等待新的消息的加入。這是一個(gè)很通用的機(jī)制。
在 Android,這里的線程分為有消息循環(huán)的線程和沒有消息循環(huán)的線程,有消息循環(huán)的線程一般都會(huì)有一個(gè)Looper,這個(gè)事android的新概念。我 們的主線程(UI線程)就是一個(gè)消息循環(huán)的線程。針對(duì)這種消息循環(huán)的機(jī)制,我們引入一個(gè)新的機(jī)制Handle,我們有消息循環(huán),就要往消息循環(huán)里面發(fā)送相 應(yīng)的消息,自定義 消息一般都會(huì)有自己對(duì)應(yīng)的處理,消息的發(fā)送和清除,消息的的處理,把這些都封裝在Handle里面,注意Handle只是針對(duì)那些有Looper的線程,不管是UI線程還是子線程,只要你有Looper,我就可以往你的消息隊(duì)列里面添加?xùn)|西,并做相應(yīng)的處理。
但是這里還有一點(diǎn),就是只要是關(guān)于UI相關(guān)的東西,就不能放在子線程中,因?yàn)樽泳€程是不能操作UI的,只能進(jìn)行數(shù)據(jù)、系統(tǒng) 等其他非UI的操作。
那么什么情況下面我們的子線程才能看做是一個(gè)有Looper的線程呢?我們?nèi)绾蔚玫剿麹ooper的句柄呢?
Looper.myLooper();獲得當(dāng)前的Looper
Looper.getMainLooper () 獲得UI線程的Lopper
我們看看Handle的初始化函數(shù),如果沒有參數(shù),那么他就默認(rèn)使用的是當(dāng)前的Looper,如果有Looper參數(shù),就是用對(duì)應(yīng)的線程的Looper。
如 果一個(gè)線程中調(diào)用Looper.prepare(),那么系統(tǒng)就會(huì)自動(dòng)的為該線程建立一個(gè)消息隊(duì)列,然后調(diào)用 Looper.loop();之后就進(jìn)入了消息循環(huán),這個(gè)之后就可以發(fā)消息、取消息、和處理消息。這個(gè)如何發(fā)送消息和如何處理消息可以再其他的線程中通過 Handle來做,但前提是我們的Hanle知道這個(gè)子線程的Looper,但是你如果不是在子線程運(yùn)行 Looper.myLooper(),一般是得不到子線程的looper的。
public void run() {
synchronized (mLock) {
Looper.prepare();
//do something
}
Looper.loop();
}
所以很多人都是這樣做的:我直接在子線程中新建handle,然后在子線程中發(fā)送消息,這樣的話就失去了我們多線程的意義了。
class myThread extends Thread{
private EHandler mHandler ;
public void run() {
Looper myLooper, mainLooper;
myLooper = Looper.myLooper ();
mainLooper = Looper.getMainLooper ();
String obj;
if (myLooper == null ){
mHandler = new EHandler(mainLooper);
obj = "current thread has no looper!" ;
}
else {
mHandler = new EHandler(myLooper);
obj = "This is from current thread." ;
}
mHandler .removeMessages(0);
Message m = mHandler .obtainMessage(1, 1, 1, obj);
mHandler .sendMessage(m);
}
}
可以讓其他的線程來控制我們的handle,可以把 private EHandler mHandler ;放在外面,這樣我們的發(fā)消息和處理消息都可以在外面來定義,這樣增加程序 代碼 的美觀,結(jié)構(gòu)更加清晰。
對(duì)如任何的Handle,里面必須要重載一個(gè)函數(shù)
public void handleMessage(Message msg)
這個(gè)函數(shù)就是我們的消息處理,如何處理,這里完全取決于你,然后通過 obtainMessage和 sendMessage等來生成和發(fā)送消息, removeMessages(0)來清除消息隊(duì)列。Google 真是太智慧了,這種框架 的產(chǎn)生,我們寫代碼更加輕松了。
有的時(shí)候,我們的子線程想去改變UI了,這個(gè)時(shí)候千萬不要再子線程中去修改,獲得UI線程的Looper,然后發(fā)送消息即可。
我們看看Goole Music App的源代碼 。
在MediaPlaybackActivity.java 中,我們可以看一下再OnCreate中的有這樣的兩句:
mAlbumArtWorker = new Worker("album art worker");
mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());
很 明顯這兩句,是構(gòu)建了一個(gè)子線程。并且這個(gè)子線程還是Looper的子線程,這里很牛逼的使用了 mAlbumArtWorker.getLooper()這個(gè)函數(shù),因?yàn)槲覀冎溃覀兡軌虻玫阶泳€程的Looper的途徑只有一個(gè):就是在子線程中調(diào)用 Looper.myLooper (),并且這個(gè)函數(shù)還要在我們perpare之后調(diào)用才能得到正確的Looper,但是他這里用了一個(gè)這樣的什么東東 getLooper,不知道它是如何實(shí)現(xiàn)的?
這里有一個(gè)大概的思路,我們?cè)谧泳€程的的prepare之后調(diào)用 myLooper ()這個(gè)方法,然后保存在一個(gè)成員變量中,這個(gè)getLooper就返回這個(gè)東西,但是這里會(huì)碰到多線程的一個(gè)很突出的問題,同步。我們?cè)诟妇€程中調(diào)用 mAlbumArtWorker.getLooper(),但是想要這個(gè)返回正確的looper就必須要求我們的子線程運(yùn)行了prepare,但是這個(gè)東 西實(shí)在子線程運(yùn)行的,我們?nèi)绾伪WC呢?
我們看Google是如何實(shí)現(xiàn)的?
private class Worker implements Runnable {
private final Object mLock = new Object();
private Looper mLooper;
/**
* Creates a worker thread with the given name. The thread
* then runs a [email=%7B@link]{@link [/email] android.os.Looper}.
* @param name A name for the new thread
*/
Worker(String name) {
Thread t = new Thread(null, this, name);
t.setPriority(Thread.MIN_PRIORITY);
t.start();
synchronized (mLock) {
while (mLooper == null) {
try {
mLock.wait();
} catch (InterruptedException ex) {
}
}
}
}
public Looper getLooper() {
return mLooper;
}
public void run() {
synchronized (mLock) {
Looper.prepare();
mLooper = Looper.myLooper();
mLock.notifyAll();
}
Looper.loop();
}
public void quit() {
mLooper.quit();
}
}
我 們知道,一個(gè)線程類的構(gòu)造函數(shù)是在主線程中完成的,所以在我們的 Worker的構(gòu)造函數(shù)中我們創(chuàng)佳一個(gè)線程,然后讓這個(gè)線程運(yùn)行,這一這個(gè)線程的創(chuàng)建是指定一個(gè) Runnabl,這里就是我們的Worker本身,在主線程調(diào)用 t.start();,這后,我們子線程已經(jīng)創(chuàng)建,并且開始執(zhí)行work的run方法。然后下面的代碼很藝術(shù):
synchronized (mLock) {
while (mLooper == null) {
try {
mLock.wait();
} catch (InterruptedException ex) {
}
}
}
我們開始等待我們的子線程給mLooper賦值,如果不賦值我們就繼續(xù)等,然后我們的子線程在運(yùn)行run方法之后,在給 mLooper賦值之后,通知worker夠著函數(shù)中的wait,然后我們的構(gòu)造函數(shù)才能完成,所以我們說:
mAlbumArtWorker = new Worker("album art worker");
這句本身就是阻塞的,它創(chuàng)建了一個(gè)子線程,開啟了子線程,并且等待子線程給mLooper賦值,賦值完成之后,這個(gè)函數(shù)才返回,這樣才能保證我們的子線程的Looper的獲取 絕對(duì)是正確的,這個(gè)構(gòu)思很有創(chuàng)意。值得借鑒
Android中Handler的使用方法——在子線程中更新界面
本文主要介紹Android的Handler的使用方法。Handler可以發(fā)送Messsage和Runnable對(duì)象到與其相關(guān)聯(lián)的線程的消息隊(duì)列。每個(gè)Handler對(duì)象與創(chuàng)建它的線程相關(guān)聯(lián),并且每個(gè)Handler對(duì)象只能與一個(gè)線程相關(guān)聯(lián)。
1. Handler一般有兩種用途:1)執(zhí)行計(jì)劃任務(wù),你可以再預(yù)定的實(shí)現(xiàn)執(zhí)行某些任務(wù),可以模擬定時(shí)器。2)線程間通信。在Android的應(yīng)用啟動(dòng)時(shí),會(huì) 創(chuàng)建一個(gè)主線程,主線程會(huì)創(chuàng)建一個(gè)消息隊(duì)列來處理各種消息。當(dāng)你創(chuàng)建子線程時(shí),你可以再你的子線程中拿到父線程中創(chuàng)建的Handler對(duì)象,就可以通過該 對(duì)象向父線程的消息隊(duì)列發(fā)送消息了。由于Android要求在UI線程中更新界面,因此,可以通過該方法在其它線程中更新界面。
◆ 通過Runnable在子線程中更新界面的例子
1.○ 在onCreate中創(chuàng)建Handler
public class HandlerTestApp extends Activity {
Handler mHandler;
TextView mText;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mHandler = new Handler();//創(chuàng)建Handler
mText = (TextView) findViewById(R.id.text0);//一個(gè)TextView
}
○ 構(gòu)建Runnable對(duì)象,在runnable中更新界面,此處,我們修改了TextView的文字.此處需要說明的是,Runnable對(duì)象可以再主線程中創(chuàng)建,也可以再子線程中創(chuàng)建。我們此處是在子線程中創(chuàng)建的。
Runnable mRunnable0 = new Runnable()
{
@Override
public void run() {
mText.setText("This is Update from ohter thread, Mouse DOWN");
}
};
? ○ 創(chuàng)建子線程,在線程的run函數(shù)中,我們向主線程的消息隊(duì)列發(fā)送了一個(gè)runnable來更新界面。
private void updateUIByRunnable(){
new Thread()
{
//Message msg = mHandler.obtainMessage();
public void run()
{
//mText.setText("This is Update from ohter thread, Mouse DOWN");//這句將拋出異常
mHandler.post(mRunnable0);
}
}.start();
}
◆ 用Message在子線程中來更新界面
1. 用Message更新界面與Runnable更新界面類似,只是需要修改幾個(gè)地方。
○ 實(shí)現(xiàn)自己的Handler,對(duì)消息進(jìn)行處理
private class MyHandler extends Handler
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what)
{
case UPDATE ://在收到消息時(shí),對(duì)界面進(jìn)行更新
mText.setText("This update by message");
break;
}
}
}
○ 在新的線程中發(fā)送消息
private void updateByMessage()
{
//匿名對(duì)象
new Thread()
{
public void run()
{
//mText.setText("This is Update from ohter thread, Mouse DOWN");
//UPDATE是一個(gè)自己定義的整數(shù),代表了消息ID
Message msg = mHandler.obtainMessage(UPDATE);
mHandler.sendMessage(msg);
}
}.start();
}