異步消息處理機(jī)制Handler

異步消息處理線程啟動(dòng)后會(huì)進(jìn)入一個(gè)無限的循環(huán)體之中纲菌,每循環(huán)一次贾陷,從其內(nèi)部的消息隊(duì)列中取出一個(gè)消息,然后回調(diào)相應(yīng)的消息處理函數(shù)卿堂,執(zhí)行完成一個(gè)消息后則繼續(xù)循環(huán)束莫。若消息隊(duì)列為空,線程則會(huì)阻塞等待草描。


解析異步消息處理機(jī)制Handler

Android中的異步消息處理主要由四個(gè)部分組成览绿,Message、Handler陶珠、MessageQueue和Looper挟裂。

  1. Message
    Message是在線程之間傳遞的消息,它可以在內(nèi)部攜帶少量的信息揍诽,用于在不同線程之間交換數(shù)據(jù)诀蓉。上一小節(jié)我們使用到了Message的what字段栗竖,初次之外還可以使用arg1和arg2字段來攜帶一些整型數(shù)據(jù),使用obj字段攜帶一個(gè)Object對(duì)象渠啤。
  2. Handler
    Handler顧名思義也就是處理者的意思狐肢,它主要是用于發(fā)送和處理消息的。發(fā)送消息一般是使用Handler的sendMessage()方法沥曹,而發(fā)出的消息經(jīng)過一系列地輾轉(zhuǎn)處理后份名,最終會(huì)傳遞到Handler的handleMessage()方法中。
  3. MessageQueue
    MessageQueue是消息隊(duì)列的意思妓美,它主要是用于存放所有的Handler發(fā)送的消息僵腺。這部分消息會(huì)一直存在于消息隊(duì)列中,等待被處理壶栋。每個(gè)線程中只有一個(gè)MessageQueue對(duì)象菱鸥。
    4.Looper
    Looper是每個(gè)線程中的MessageQueue的管家近上,調(diào)用Looper的loop()方法后撮珠,就會(huì)進(jìn)入到一個(gè)無限循環(huán)當(dāng)中攀操,然后每當(dāng)發(fā)現(xiàn)MessageQueue中存在一條消息,就會(huì)將它取出毙玻,并傳遞到Handler的handleMessage()方法中豌蟋。每個(gè)線程中也只會(huì)有一個(gè)Looper對(duì)象。

首先需要在主線程當(dāng)中創(chuàng)建一個(gè)Handler對(duì)象桑滩,并重寫handleMessage()方法梧疲。然后當(dāng)子線程中需要進(jìn)行UI操作時(shí),就創(chuàng)建一個(gè)Message對(duì)象施符,并通過Handler將這條信息發(fā)送出去往声。之后這條消息會(huì)被添加到MessageQueue的隊(duì)列中等待被處理,而Looper則會(huì)一直嘗試從MessageQueue中取出待處理消息戳吝,最后分發(fā)回Handler的handleMessage()方法中浩销。由于Handler是在主線程中創(chuàng)建的,所以此時(shí)handleMessage()方法中的代碼也會(huì)在主線程運(yùn)行听哭,于是我們?cè)谶@里就可以安心地進(jìn)行UI操作了慢洋。


Android異步消息處理機(jī)制讓你深入理解Looper、Handler陆盘、Message三者關(guān)系

Looper主要是prepare()和loop()兩個(gè)方法

一個(gè)線程中只有一個(gè)Looper實(shí)例
looper方法必須在prepare方法之后運(yùn)行普筹。

Looper主要作用:

1、 與當(dāng)前線程綁定隘马,保證一個(gè)線程只會(huì)有一個(gè)Looper實(shí)例太防,同時(shí)一個(gè)Looper實(shí)例也只有一個(gè)MessageQueue。
2酸员、 loop()方法蜒车,不斷從MessageQueue中去取消息讳嘱,交給消息的target屬性的dispatchMessage去處理。好了酿愧,我們的異步消息處理線程已經(jīng)有了消息隊(duì)列(MessageQueue)沥潭,也有了在無限循環(huán)體中取出消息的哥們,現(xiàn)在缺的就是發(fā)送消息的對(duì)象了嬉挡,于是乎:Handler登場(chǎng)了钝鸽。

Handler

使用Handler之前,我們都是初始化一個(gè)實(shí)例庞钢,比如用于更新UI線程拔恰,我們會(huì)在聲明的時(shí)候直接初始化,或者在onCreate中初始化Handler實(shí)例

1基括、首先Looper.prepare()在本線程中保存一個(gè)Looper實(shí)例仁连,然后該實(shí)例中保存一個(gè)MessageQueue對(duì)象;因?yàn)長(zhǎng)ooper.prepare()在一個(gè)線程中只能調(diào)用一次阱穗,所以MessageQueue在一個(gè)線程中只會(huì)存在一個(gè)。
2使鹅、Looper.loop()會(huì)讓當(dāng)前線程進(jìn)入一個(gè)無限循環(huán)揪阶,不端從MessageQueue的實(shí)例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法患朱。
3鲁僚、Handler的構(gòu)造方法,會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例裁厅,進(jìn)而與Looper實(shí)例中的MessageQueue想關(guān)聯(lián)冰沙。
4、Handler的sendMessage方法执虹,會(huì)給msg的target賦值為handler自身拓挥,然后加入MessageQueue中。
5袋励、在構(gòu)造Handler實(shí)例時(shí)侥啤,我們會(huì)重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法茬故。
好了盖灸,總結(jié)完成,大家可能還會(huì)問磺芭,那么在Activity中赁炎,我們并沒有顯示的調(diào)用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功創(chuàng)建呢钾腺,這是因?yàn)樵贏ctivity的啟動(dòng)代碼中徙垫,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法

在主線程中可以直接創(chuàng)建Handler對(duì)象讥裤,而在子線程中需要先調(diào)用Looper.prepare()才能創(chuàng)建Handler對(duì)象。


1松邪、首先Looper.prepare()在本線程中保存一個(gè)Looper實(shí)例坞琴,然后該實(shí)例中保存一個(gè)MessageQueue對(duì)象;因?yàn)長(zhǎng)ooper.prepare()在一個(gè)線程中只能調(diào)用一次逗抑,所以MessageQueue在一個(gè)線程中只會(huì)存在一個(gè)剧辐。大家可能還會(huì)問,那么在Activity中邮府,我們并沒有顯示的調(diào)用Looper.prepare()和Looper.loop()方法荧关,為啥Handler可以成功創(chuàng)建呢,這是因?yàn)樵贏ctivity的啟動(dòng)代碼中褂傀,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法
2忍啤、Looper.loop()會(huì)讓當(dāng)前線程進(jìn)入一個(gè)無限循環(huán),不端從MessageQueue的實(shí)例中讀取消息仙辟,然后回調(diào)msg.target.dispatchMessage(msg)方法同波。
3、Handler的構(gòu)造方法叠国,會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例未檩,并與Looper實(shí)例中的MessageQueue相關(guān)聯(lián)。
4粟焊、Handler的sendMessage方法冤狡,會(huì)給msg的target賦值為handler自身,然后加入MessageQueue中项棠。
5悲雳、在構(gòu)造Handler實(shí)例時(shí),我們會(huì)重寫handleMessage方法香追,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法合瓢。


Handler+Looper+MessageQueue深入詳解

概述:Android使用消息機(jī)制實(shí)現(xiàn)線程間的通信,線程通過Looper建立自己的消息循環(huán)透典,MessageQueue是FIFO的消息隊(duì)列歪玲,Looper負(fù)責(zé)從MessageQueue中取出消息,并且分發(fā)到消息指定目標(biāo)Handler對(duì)象掷匠。Handler對(duì)象綁定到線程的局部變量Looper滥崩,封裝了發(fā)送消息和處理消息的接口。

實(shí)例:我們先介紹Android線程通訊的一個(gè)例子讹语,這個(gè)例子實(shí)現(xiàn)點(diǎn)擊按鈕之后從主線程發(fā)送消息"hello"到另外一個(gè)名為” CustomThread”的線程钙皮。


package
import

public class LooperThreadActivity extends Activity{
/** Called when the activity is first created. */

private final int MSG_HELLO = 0;
private Handler mHandler;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new CustomThread().start();  //新建并啟動(dòng)CustomThread實(shí)例

findViewById(R.id.send_btn).setOnClickListener(new OnClickListener(){
@Override
        public void onClick(View v) {//點(diǎn)擊界面時(shí)發(fā)送消息
            String str = "hello";
            Log.d("Test", "MainThread is ready to send msg:" + str);
            mHandler.obtainMessage(MSG_HELLO, str).sendToTarget();//發(fā)送消息到CustomThread實(shí)例
             
        }
    });
}

class CustomThread extends Thread{
@Override
public void run() {
 //建立消息循環(huán)的步驟
        Looper.prepare();//1、初始化Looper
        mHandler = new Handler(){//2、綁定handler到CustomThread實(shí)例的Looper對(duì)象
            public void handleMessage (Message msg) {//3短条、定義處理消息的方法
                switch(msg.what) {
                case MSG_HELLO:
                    Log.d("Test", "CustomThread receive msg:" + (String) msg.obj);
                }
            }
        };
        Looper.loop();//4导匣、啟動(dòng)消息循環(huán)
    }
}
}

我們看到,為一個(gè)線程建立消息循環(huán)有四個(gè)步驟:

  1. 初始化Looper

  2. 綁定handler到CustomThread實(shí)例的Looper對(duì)象

  3. 定義處理消息的方法

  4. 啟動(dòng)消息循環(huán)

  5. 初始化Looper:Looper.prepare()
    一個(gè)線程在調(diào)用Looper的靜態(tài)方法prepare()時(shí)茸时,這個(gè)線程會(huì)新建一個(gè)Looper對(duì)象贡定,并放入到線程的局部變量中,而這個(gè)變量是不和其他線程共享的可都。

在Looper的構(gòu)造函數(shù)中缓待,創(chuàng)建了一個(gè)消息隊(duì)列對(duì)象mQueue,此時(shí),調(diào)用Looper.prepare()的線程就建立起一個(gè)消息循環(huán)的對(duì)象(此時(shí)還沒開始進(jìn)行消息循環(huán))渠牲。

  1. 綁定handler到CustomThread實(shí)例的Looper對(duì)象:mHandler = new Handler()

Handler通過mLooper= Looper.myLooper();綁定到線程的局部變量Looper上去旋炒,同時(shí)Handler通過mQueue = mLooper.mQueue;獲得線程的消息隊(duì)列。此時(shí)签杈,Handler就綁定到創(chuàng)建此Handler對(duì)象的線程的消息隊(duì)列上了瘫镇。

  1. 定義處理消息的方法:Override public void handleMessage(Message msg){}
    子類需要覆蓋這個(gè)方法,實(shí)現(xiàn)接收到消息后的處理方法答姥。

  2. 啟動(dòng)消息循環(huán):Looper.loop()


使用Thread+Handler實(shí)現(xiàn)非UI線程更新UI界面

public class ThreadhandlerActivity extends Activity{
private static final int MSG_SUCCESS = 0;
private static final int MSG_FAILURE = 1;

private Thread mThread;

private Handler mHandler = new Handler(){
public void handleMessage(Message msg){//此方法在ui線程運(yùn)行
switch(msg.what) {
case MSG_SUCCESS:
 mImageView.setImageBitmap((Bitmap) msg.obj);//imageview顯示從網(wǎng)絡(luò)獲取到的logo
            Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_success), Toast.LENGTH_LONG).show();
            break;
        case MSG_FAILURE:
            Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_failure), Toast.LENGTH_LONG).show();
            break;
        }
    }
};
  @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mImageView= (ImageView) findViewById(R.id.imageView);//顯示圖片的ImageView
    mButton = (Button) findViewById(R.id.button);
    mButton.setOnClickListener(new OnClickListener() {
         
        @Override
        public void onClick(View v) {
            if(mThread == null) {
                mThread = new Thread(runnable);
                mThread.start();//線程啟動(dòng)
            }
            else {
                Toast.makeText(getApplication(), getApplication().getString(R.string.thread_started), Toast.LENGTH_LONG).show();
            }
        }
    });
}
 
Runnable runnable = new Runnable() {
     
    @Override
    public void run() {//run()在新的線程中運(yùn)行
        HttpClient hc = new DefaultHttpClient();
        HttpGet hg = new HttpGet("http://www.oschina.net/img/logo.gif");//獲取oschina的logo
        final Bitmap bm;
        try {
            HttpResponse hr = hc.execute(hg);
            bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
        } catch (Exception e) {
            mHandler.obtainMessage(MSG_FAILURE).sendToTarget();//獲取圖片失敗
            return;
        }
        mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();//獲取圖片成功铣除,向ui線程發(fā)送MSG_SUCCESS標(biāo)識(shí)和bitmap對(duì)象

//          mImageView.setImageBitmap(bm); //出錯(cuò)!不能在非ui線程操作ui元素

//          mImageView.post(new Runnable() {//另外一種更簡(jiǎn)潔的發(fā)送消息給ui線程的方法鹦付。
//              
//              @Override
//              public void run() {//run()方法會(huì)在ui線程執(zhí)行
//                  mImageView.setImageBitmap(bm);
//              }
//          });
    }
    };
 
}

Android開發(fā)中Handler的經(jīng)典總結(jié)

當(dāng)應(yīng)用程序啟動(dòng)時(shí)通孽,Android首先會(huì)開啟一個(gè)主線程(也叫UI線程),主線程為管理界面中的UI控件睁壁,進(jìn)行事件分發(fā)。

  1. Handler定義
    主要接收子線程發(fā)送的數(shù)據(jù)互捌,并用此數(shù)據(jù)配合主線程更新UI潘明。

Android主線程是線程不安全的,更新UI只能在主線程中更新秕噪,子線程中操作是危險(xiǎn)的钳降。
Handler運(yùn)行在主線程中,它與子線程可以通過Message對(duì)象來傳遞數(shù)據(jù)腌巾,這個(gè)時(shí)候遂填,Handler就承擔(dān)著接收子線程傳過來的(子線程用sendMessage()方法傳遞)Message對(duì)象,(里面包含數(shù)據(jù))澈蝙,把這些消息放入主線程隊(duì)列中吓坚,配合主線程進(jìn)行更新UI。

  1. Handler一些特點(diǎn)
    Handler可以分發(fā)Message對(duì)象和Runnable對(duì)象到主線程中灯荧,每個(gè)Handler實(shí)例礁击,都會(huì)綁定到創(chuàng)建它的線程池中(一般是位于主線程),它有兩個(gè)作用:
    (1)安排消息或Runnable在某個(gè)主線程中某個(gè)地方執(zhí)行;
    (2)安排一個(gè)動(dòng)作在不同的線程中執(zhí)行哆窿。
    Handler中分發(fā)消息的一些方法

post(Runnable)
postAtTime(Runnable链烈,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message挚躯,long)

以上post類方法允許你安排一個(gè)Runnable對(duì)象到主線程隊(duì)列中强衡,sendMessage類方法,允許你安排一個(gè)帶數(shù)據(jù)的Message對(duì)象到隊(duì)列中码荔,等待更新漩勤。

  1. Handler實(shí)例
    子類需要繼承Handler類,并重寫handleMessage(Message msg)方法目胡,用于接收線程數(shù)據(jù)锯七。
public class MyHandlerActivity extends Activity {
Button button;
MyHandler myHandler;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handlertest);

button = (Button)     findViewById(R.id.button);
myHandler = new MyHandler();
//當(dāng)創(chuàng)建一個(gè)新的Handler實(shí)例時(shí),它會(huì)綁定到當(dāng)前線程和消息的隊(duì)列中誉己,開始分發(fā)數(shù)據(jù)
//Handler有兩個(gè)作用:(1)定時(shí)執(zhí)行Message和Runnable對(duì)象
//(2):讓一個(gè)動(dòng)作眉尸,在不同的線程中執(zhí)行。

//它安排消息巨双,用以下方法
//post(Runnable)
//postAtTime(Runnable, long)
// postDelayed(Runnable, long)
//sendEmptyMessage(int)
//sendMessage(Message)
//sendMessageAtTime(Message, long)
//sendMessageDelayed(Message, long)

//以上方法以post開頭的允許你處理Runnable對(duì)象
//sendMessage()允許你處理Message對(duì)象(Message里可以包含數(shù)據(jù))
MyThread m = new MyThread();
new Thread(m).start();
}

/**
* 接收消息噪猾,處理消息,此Handler會(huì)與當(dāng)前主線程一塊運(yùn)行
**/

class MyHandler extends Handler {
public MyHandler(){
}

public MyHandler(Looper L){
super(L);
}

//子類必須重寫此方法筑累,接收數(shù)據(jù)
@Override
public void handleMessage(Message msg){
// TODO Auto-generated method stub
Log.d("MyHandler","handleMessage....");
super.handleMessage(msg);
//此處可以更新UI
Bundle b = msg.getDada();
String color = b.getString("color");
MyHandlerActivity.this.button.append(color);
}
}

class MyThread implements Runnable{
public void run(){
try{
Thread.sleep(10000);
} catch(InterruptedException e){
//TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("thread....","mThread....");
Message msg = new Message();
Bundle b = new Bundle(); //存放數(shù)據(jù)
b.putString("color","我的");
msg.setData(b);

MyHandlerActivity.this.myHandler.sendMessage(msg);//向Handler發(fā)送消息袱蜡,更新UI
}
}
}

一個(gè)Handler允許你發(fā)送和處理消息以及與一個(gè)線程的消息隊(duì)列相關(guān)的Runnable對(duì)象。每個(gè)Handler實(shí)例都和單個(gè)線程以及該線程的消息隊(duì)列有關(guān)慢宗。當(dāng)你創(chuàng)建了一個(gè)新handler,它就會(huì)和創(chuàng)建它的線程/消息隊(duì)列綁定坪蚁,在那以后,它就會(huì)傳遞消息以及runnable對(duì)象給消息隊(duì)列镜沽,然后執(zhí)行它們敏晤。

需要使用Handler有兩大主要的原因:
(1)在將來的某個(gè)時(shí)間點(diǎn)調(diào)度處理消息和runnable對(duì)象;
(2)將需要執(zhí)行的操作放到其他線程之中缅茉,而不是自己的

若在主線程中實(shí)例化一個(gè)Handler對(duì)象嘴脾,例如:
Handler mHandler = new Handler();
此時(shí)它并沒有新派生一個(gè)線程來執(zhí)行此Handler,而是將此Handler附加在主線程上蔬墩,故此時(shí)若你在Handler中執(zhí)行耗時(shí)操作的話译打,還是會(huì)彈出ANR對(duì)話框!


創(chuàng)建Handler

  1. 使用默認(rèn)的構(gòu)造方法:new Handler()
  2. 使用帶參的構(gòu)造方法拇颅,參數(shù)是一個(gè)Runnable對(duì)象或者回調(diào)對(duì)象

Handler只是簡(jiǎn)單地往消息隊(duì)列中發(fā)送消息而已
它們有更方便的方法可以幫助與UI線程通信奏司。

在Android,線程分為有消息循環(huán)的線程和沒有消息循環(huán)的線程樟插,有消息循環(huán)的線程一般都會(huì)有一個(gè)Looper结澄。我們的主線程(UI線程)就是一個(gè)消息循環(huán)的線程哥谷。針對(duì)這種消息循環(huán)的機(jī)制,我們引入了一個(gè)新的機(jī)制Handler麻献,我們有消息循環(huán)们妥,就要往消息循環(huán)里面發(fā)送相應(yīng)的消息,自定義消息一般都會(huì)有自己對(duì)應(yīng)的處理勉吻,消息的發(fā)送和清除监婶,消息的處理,把這些都封裝在Handler里面齿桃,注意Handler只是針對(duì)那些有Looper的線程惑惶,不管是UI線程還是子線程,只要你有Looper短纵,我就可以往你的消息隊(duì)列里面添加?xùn)|西带污,并做相應(yīng)的處理。

如果在子線程里面新建handler香到,就會(huì)出現(xiàn)錯(cuò)誤鱼冀,原因就是一個(gè)線程對(duì)應(yīng)一個(gè)或零個(gè)Looper和MessageQueue。handler是一種消息機(jī)制悠就,而子線程的啟用默認(rèn)是沒有Looper對(duì)象的(主線程有)千绪,所以在子線程使用Handler對(duì)象的時(shí)候,要先使用Looper.prepare()梗脾,啟用一個(gè)Looper荸型,然后新建Handler對(duì)象,再使用Looper.loop().
至此子線程就有了自己的Looper炸茧,可以接收和處理信息瑞妇。

android.os.Looper:

Looper用于封裝了android線程中的消息循環(huán),默認(rèn)情況下一個(gè)線程是不存在消息循環(huán)(message loop)的梭冠,需要調(diào)用Looper.prepare()來給線程創(chuàng)建一個(gè)消息循環(huán)辕狰,調(diào)用Looper.loop()來使消息循環(huán)起作用,從消息隊(duì)列里取消息妈嘹,處理消息。

注:寫在Looper.loop()之后的代碼不會(huì)被立即執(zhí)行绍妨,當(dāng)調(diào)用后mHandler.getLooper().quit()后润脸,loop才會(huì)中止,其后的代碼才能得以運(yùn)行他去。Looper對(duì)象通過MessageQueue來存放消息和事件毙驯。一個(gè)線程只能有一個(gè)Looper,對(duì)應(yīng)一個(gè)MessageQueue灾测。

一個(gè)典型的Looper Thread實(shí)現(xiàn):

class LooperThread extends Thread {      
public Handler mHandler;      
public void run() {          
Looper.prepare();          
mHandler = new Handler() {              
public void handleMessage(Message msg) {                
// process incoming messages here              
}          
};          
Looper.loop();      
}  
}

android.os.Handler:

Handler用于跟線程綁定爆价,來向線程的消息循環(huán)里面發(fā)送消息、接收消息并處理消息。

通過以下函數(shù)來向線程發(fā)送消息或Runnable:

1.post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long)铭段;
當(dāng)線程接收到Runnable對(duì)象后即立即或一定延遲調(diào)用骤宣。
2.sendEmptyMessage(int), sendMessage(Message)
, sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)。
當(dāng)線程接受到這些消息后序愚,根據(jù)你的Handler重構(gòu)的handleMessage(Message)根據(jù)接收到的消息來進(jìn)行處理憔披。

一個(gè)Activity主線程中可以有多個(gè)Handler對(duì)象,但MessageQueue是只有一個(gè)爸吮,對(duì)應(yīng)的Looper也是只有一個(gè)芬膝。

Looper類的靜態(tài)成員函數(shù)prepareMainLooper是專門應(yīng)用程序的主線程調(diào)用的,為了讓其它地方能夠方便地通過Looper類的getMainLooper函數(shù)來獲得應(yīng)用程序主線程中的消息循環(huán)對(duì)象形娇。


Android Handler詳細(xì)使用方法實(shí)例

handler使用例1.

這個(gè)例子是最簡(jiǎn)單的介紹handler使用的锰霜,是將handler綁定到它所建立的線程中。

單擊Start按鈕桐早,程序會(huì)開始啟動(dòng)線程癣缅,并且線程程序完成后延時(shí)1s會(huì)繼續(xù)啟動(dòng)該線程,每次線程的run函數(shù)中完成對(duì)界面輸出nUpdateThread…文字勘畔,不停的運(yùn)行下去所灸,當(dāng)單擊End按鈕時(shí),該線程就會(huì)停止炫七,如果繼續(xù)單擊Start爬立,則文字又開始輸出了。

MainActivity.java;
主要代碼

public class MainActivity extends Activity{

//使用handler時(shí)首先要?jiǎng)?chuàng)建一個(gè)handler對(duì)象
Handler  handler = new Handler();
//要用Handler來處理多線程可以使用Runnable接口万哪,這里先定義該接口
//線程中運(yùn)行該接口的run函數(shù)
Runnable update_thread = new Runnable()
{
public void run()
{
handler.postDelayed(update_thread,1000);
}
};
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState    );
...
}
//btn監(jiān)聽實(shí)現(xiàn)
//調(diào)用handler的post方法侠驯,將要執(zhí)行的線程對(duì)象添加到隊(duì)列當(dāng)中
//將線程接口立刻送到線程隊(duì)列
handler.post(update_thread);
//將接口從線程隊(duì)列中移除
handler.removeCallbacks(update_thread);
}

post方法雖然發(fā)送的是一個(gè)實(shí)現(xiàn)了Runnable接口的類對(duì)象奕巍,但是它并非創(chuàng)建了一個(gè)新線程吟策,而是執(zhí)行了該對(duì)象中的run方法。也就是說的止,整個(gè)run中的操作和主線程處于同一個(gè)線程檩坚。

為了解決這個(gè)問題,就需要使得handler綁定到一個(gè)新開啟線程的消息隊(duì)列上诅福,在這個(gè)處于另外線程上的消息隊(duì)列中處理傳過來的Runnable對(duì)象和消息匾委。

這個(gè)例子將學(xué)會(huì)怎樣不使用runnable來啟動(dòng)一個(gè)線程,而是用HandlerThread的looper來構(gòu)造一個(gè)handler氓润,然后該handler自己獲得消息赂乐,并傳遞數(shù)據(jù),然后又自己處理消息咖气,當(dāng)然這是在另一個(gè)線程中完成的挨措。
消息結(jié)構(gòu)中傳遞簡(jiǎn)單的整型可以采用它的參數(shù)arg1和arg2挖滤,或者傳遞一些小的其它數(shù)據(jù),可以用它的object浅役,該object可以是任意的對(duì)象斩松。當(dāng)需要傳送比較大的數(shù)據(jù)是,可以使用消息的setData方法担租,該方法需要傳遞一個(gè)Bundle的參數(shù)砸民。Bundle中存放的是鍵值對(duì)的map,只是它的鍵值類型和數(shù)據(jù)類型比較固定而已奋救。

public class HandleTest2 extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState  );
setContentView(R.layout.main);
//生成一個(gè)HandlerThread對(duì)象
HandlerThread handlerThread = new HandlerThread("handler_thread");
//在使用HandlerThread的getLooper()方法之前岭参,必須先調(diào)用該類的start(),同時(shí)開啟一個(gè)新線程尝艘;
handlerThread.start();
//將由HandlerThread獲取的Looper傳遞給Handler對(duì)象演侯,即由處于另外線程的Looper代替handler初始化時(shí)默認(rèn)綁定的消息隊(duì)列來處理消息。
//HandlerThread顧名思義就是好可以處理消息循環(huán)的線程背亥,它是一個(gè)擁有Looper的線程秒际,可以處理消息循環(huán);其實(shí)與其說Handler和一個(gè)線程綁定狡汉,倒不如說Handler和Looper是一一對(duì)應(yīng)的娄徊。
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
Message msg = myHandler.obtainMessage();
//將msg發(fā)送到目標(biāo)對(duì)象,所謂的目標(biāo)對(duì)象盾戴,就是生成該msg對(duì)象的handler對(duì)象
Bundle b = new Bundle();
b.putInt("age",20);
b.putString("name","Jhon");
msg.setData(b);
//將msg發(fā)送到自己的handler中
msg.sendToTarget(); //將msg發(fā)送到myHandler
}

//定義類
class MyHandler extends     Handler{
public MyHandler(){
}
public MyHandler(Looper looper){
super(looper);
}

@Override
public void handleMessage(Message msg){
Bundle b = msg.getData();
int age = b.getInt("age");
String name =     b.getString("name");
}
}
}

這樣寄锐,當(dāng)使用sendMessage方法傳遞消息或者使用post方法傳遞Runnable對(duì)象時(shí),就會(huì)把它們傳遞到與handler對(duì)象綁定的處于另外一個(gè)線程的消息隊(duì)列中尖啡,它們將在另外的消息隊(duì)列中被處理橄仆。而主線程還會(huì)在發(fā)送完成時(shí)候繼續(xù)進(jìn)行,不會(huì)影響當(dāng)前的操作衅斩。
這里需要注意盆顾,這里用到的多線程并非由Runnable對(duì)象開啟的,而是ThreadHandler對(duì)象開啟的畏梆。Runnable對(duì)象只是作為一個(gè)封裝了操作的對(duì)象被傳遞您宪,并未產(chǎn)生新線程。

android中handler用法總結(jié)

另外再?gòu)?qiáng)調(diào)一遍奠涌,在UI線程(主線程)中:

mHandler=new Handler();
mHandler.post(new Runnable(){
void run(){
   //執(zhí)行代碼..
 }
});
這個(gè)線程其實(shí)是在UI線程之內(nèi)運(yùn)行的宪巨,并沒有新建線程。

常見的新建線程的方法是:

Thread thread = new Thread();
thread.start();

HandlerThread thread = new HandlerThread("string");
thread.start();

Handler+Looper+MessageQueue深入詳解

1.主線程給自己發(fā)送Message

Looper looper = Looper.getMainLooper()铣猩;//獲取主線程的Looper對(duì)象
//這里以主線程的Looper對(duì)象創(chuàng)建了handler揖铜, 
//所以茴丰,這個(gè)handler發(fā)送的Message會(huì)被傳遞給主線程的MessageQueue达皿。
handler = new MyHandler(looper);     
handler.removeMessages(0);
//構(gòu)建Message對(duì)象 
//第一個(gè)參數(shù):是自己指定的message代號(hào)天吓,方便在handler選擇性地接收 
//第二三個(gè)參數(shù)沒有什么意義 
//第四個(gè)參數(shù)需要封裝的對(duì)象
Message msg = handler.obtainMessage(1,1,1,"someword");
handler.sendMessage(msg);//發(fā)送消息

2.其他線程給主線程發(fā)送Message

public class MainActivity extends Activity {
private Handler handler;
//可以看出這里啟動(dòng)了一個(gè)線程來操作消息的封裝和發(fā)送的工作
new MyThread().start(); 

//加載一個(gè)線程類
class Mythread extends Thread{
public void run(){
Looper looper = Looper.getMainLooper();//主線程的Looper對(duì)象
//這里以主線程的Looper對(duì)象創(chuàng)建了handler
//所以,這個(gè)handler發(fā)送的Message會(huì)被傳遞給主線程的MessageQueue
handler = new MyHandler(looper);
//構(gòu)建Message對(duì)象 
//第一個(gè)參數(shù):是自己指定的message代號(hào)峦椰,方便在handler選擇性地接收 
//第二三個(gè)參數(shù)沒有什么意義 
//第四個(gè)參數(shù)需要封裝的對(duì)象 
Message msg = handler.obtainMessage(1,1,1,"其他線程發(fā)消息了"); 
handler.sendMessage(msg); //發(fā)送消息 
} 
}
}
  1. 主線程給其他線程發(fā)送Message
public class MainActivity extends Activity { 
private Button btnTest; 
private TextView textView; 
private Handler handler; 
@Override 
public void onCreate(Bundle savedInstanceState) {     
super.onCreate(savedInstanceState);     
setContentView(R.layout.main); 
btnTest = (Button)this.findViewById(R.id.btn_01); 
textView = (TextView)this.findViewById(R.id.view_01); 
//啟動(dòng)線程 
new MyThread().start(); 
btnTest.setOnClickListener(new View.OnClickListener() {     
@Override 
public void onClick(View arg0) { 
//這里handler的實(shí)例化在線程中 
//線程啟動(dòng)的時(shí)候就已經(jīng)實(shí)例化了 
Message msg = handler.obtainMessage(1,1,1,"主線程發(fā)送的消息"); 
handler.sendMessage(msg); 
} 
}); 
} 

class MyHandler extends Handler{ 
public MyHandler(Looper looper){ 
super(looper); 
} 
public void handleMessage(Message msg){     
super.handleMessage(msg); 
textView.setText("我是主線程的Handler龄寞,收到了消息:"+    (String)msg.obj); 
} 
} 
class MyThread extends Thread{ 
public void run(){ 
Looper.prepare(); //創(chuàng)建該線程的Looper對(duì)象,用于接收消息 
//注意了:這里的handler是定義在主線程中的哦汤功,呵呵物邑, 
//前面看到直接使用了handler對(duì)象,是不是在找滔金,在什么地方實(shí)例化的呢色解? 
//現(xiàn)在看到了吧?餐茵?科阎?呵呵,開始的時(shí)候?qū)嵗涣朔拮澹驗(yàn)樵摼€程的Looper對(duì)象 
//還不存在呢÷啾浚現(xiàn)在可以實(shí)例化了 
//這里L(fēng)ooper.myLooper()獲得的就是該線程的Looper對(duì)象了     
handler = new ThreadHandler(Looper.myLooper()); 
//這個(gè)方法,有疑惑嗎道批? 
//其實(shí)就是一個(gè)循環(huán)错英,循環(huán)從MessageQueue中取消息。 
//不經(jīng)常去看看隆豹,你怎么知道你有新消息呢椭岩??噪伊? 
Looper.loop(); 
} 
//定義線程類中的消息處理類 
class ThreadHandler extends Handler{ 
public ThreadHandler(Looper looper){ 
super(looper); 
} 
public void handleMessage(Message msg){ 
//這里對(duì)該線程中的MessageQueue中的Message進(jìn)行處理 
//這里我們?cè)俜祷亟o主線程一個(gè)消息 
handler = new MyHandler(Looper.getMainLooper()); 
Message msg2 = handler.obtainMessage(1,1,1,"子線程收到:"+(String)msg.obj); 
handler.sendMessage(msg2); 
} 
} 
}
}

多個(gè)任務(wù)的話簿煌,還有一個(gè)問題,這里是并行還是串行執(zhí)行鉴吹?

Android 中Message,MessageQueue,Looper,Handler詳解+實(shí)例
http://tanghaibo001.blog.163.com/blog/static/9068612020111287218197/


Android 異步消息處理機(jī)制 讓你深入理解 Looper姨伟、Handler、Message三者關(guān)系
http://blog.csdn.net/lmj623565791/article/details/38377229?utm_source=tuicool&utm_medium=referral

Android開發(fā)中Handler的經(jīng)典總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末豆励,一起剝皮案震驚了整個(gè)濱河市夺荒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌良蒸,老刑警劉巖技扼,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嫩痰,居然都是意外死亡剿吻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門串纺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丽旅,“玉大人椰棘,你說我怎么就攤上這事¢希” “怎么了邪狞?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)茅撞。 經(jīng)常有香客問我帆卓,道長(zhǎng),這世上最難降的妖魔是什么米丘? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任剑令,我火速辦了婚禮,結(jié)果婚禮上拄查,老公的妹妹穿的比我還像新娘尚洽。我一直安慰自己,他們只是感情好靶累,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布腺毫。 她就那樣靜靜地躺著,像睡著了一般挣柬。 火紅的嫁衣襯著肌膚如雪潮酒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天邪蛔,我揣著相機(jī)與錄音急黎,去河邊找鬼。 笑死侧到,一個(gè)胖子當(dāng)著我的面吹牛勃教,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匠抗,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼故源,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了汞贸?” 一聲冷哼從身側(cè)響起绳军,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎矢腻,沒想到半個(gè)月后门驾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡多柑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年奶是,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡聂沙,死狀恐怖腐魂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逐纬,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布削樊,位于F島的核電站豁生,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏漫贞。R本人自食惡果不足惜甸箱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望迅脐。 院中可真熱鬧芍殖,春花似錦、人聲如沸谴蔑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隐锭。三九已至窃躲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钦睡,已是汗流浹背蒂窒。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荞怒,地道東北人洒琢。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像褐桌,于是被迫代替她去往敵國(guó)和親衰抑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容