天天看點

Android中對Handle機制的了解

一、重要參考資料 

【參考資料】 

   目前來看,下面的幾個網址中的内容品質比較不錯,基本不需要再讀别的網址了。 

1、android消息機制一 

 android消息機制(一) 

一、    角色描述 

1.looper: 一個線程可以産生一個looper對象,由它來管理此線程裡的message queue(消息隊列)。 

2.handler: 你可以構造handler對象來與looper溝通,以便push新消息到message queue裡;或者接收looper(從message queue取出)所送來的消息。

3. message queue(消息隊列):用來存放線程放入的消息。 

4.線程:ui thread 通常就是main thread,而android啟動程式時會替它建立一個message queue。 

每一個線程裡可含有一個looper對象以及一個messagequeue資料結構。在你的應用程式裡,可以定義handler的子類别來接收looper所送出的消息。

在你的android程式裡,新誕生一個線程,或執行 (thread)時,并不會自動建立其message loop。 

android裡并沒有global的message queue資料結構,例如,不同apk裡的對象不能透過massage queue來交換訊息(message)。 例如:線程a的handler對象可以傳遞消息給别的線程,讓别的線程b或c等能送消息來給線程a(存于a的message queue裡(//如何了解?))。

線程a的message queue裡的訊息,隻有線程a所屬的對象可以處理。 

使用looper.mylooper可以取得目前線程的looper對象。 

使用mhandler = new eevnthandler(looper.mylooper()); 可用來構造目前線程的handler對象;其中,eevnthandler是自已實作的handler的子類别。

使用mhandler = new eevnthandler(looper.getmainlooper()); 可誕生用來處理main線程的handler對象;其中,eevnthandler是自已實作的handler的子類别。

這樣描述可能太抽像,下面舉幾個實際的例子來說明: 

二、    舉例 

1.  同線程内不同元件間的消息傳遞 

looper類用來管理特定線程内對象之間的消息交換(message exchange)。你的應用程式可以産生許多個線程。而一個線程可以有許多個元件,這些元件之間常常需要互相交換訊息。如果有這種需要,您可以替線程構造一個looper對象,來擔任訊息交換的管理工作。looper對象會建立一個messagequeue資料結構來存放各對象傳來的消息(包括ui事件或system事件等)。如下圖:

每一個線程裡可含有一個looper對象以及一個messagequeue資料結構。在你的應用程式裡,可以定義handler的子類别來接收looper所送出的消息。 

同線程不同元件之間的消息傳遞: 

Android中對Handle機制的了解

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: //元件1  

                     looper looper = looper.mylooper();//取得目前線程裡的looper   

                     myhandler mhandler = new myhandler(looper);//構造一個handler使之可與looper通信  

                     //buton等元件可以由mhandler将消息傳給looper後,再放入messagequeue中,同時mhandler也可以接受來自looper消息  

                     mhandler.removemessages(0);   

                     string msgstr = "主線程不同元件通信:消息來自button";   

                     message m = mhandler.obtainmessage(1, 1, 1, msgstr);//構造要傳遞的消息  

                     mhandler.sendmessage(m);//發送消息:系統會自動調用handlemessage方法來處理消息   

                     break;   

              }               

       }        

       private class myhandler extends handler{                

              public myhandler(looper looper){   

                     super(looper);   

              }   

              @override   

              public void handlemessage(message msg) {//處理消息   

                     text.settext(msg.obj.tostring()); //元件2  

}   

說明: 

    looper = looper.mylooper ();  

調用looper類别的靜态mylooper()函數,以取得目前線程裡的looper對象. 

mhandler = new myhandler (looper); 

構造一個myhandler對象來與looper溝通。activity等對象可以藉由myhandler對象來将消息傳給looper,然後放入messagequeue裡;myhandler對象也扮演listener的角色,可接收looper對象所送來的消息。

message m = mhandler.obtainmessage(1, 1, 1, obj); 

先構造一個message對象,并将資料存入對象裡。 

mhandler.sendmessage(m); 

就透過mhandler對象而将消息m傳給looper,然後放入messagequeue裡。 

此時,looper對象看到messagequeue裡有消息m,就将它廣播出去,mhandler對象接到此訊息時,會呼叫其handlemessage()函數來處理,于是輸出"this my message!"于畫面上,

android消息處理機制(二)

角色綜述(回顧):

   (1)ui thread 通常就是main thread,而android啟動程式時會替它建立一個messagequeue。

(2)當然需要一個looper對象,來管理該messagequeue。

(3)我們可以構造handler對象來push新消息到message queue裡;或者接收looper(從message queue取出)所送來的消息。

(4)線程a的handler對象可以傳遞給别的線程,讓别的線程b或c等能送訊息來給線程a(存于a的message queue裡)。

(5)線程a的message queue裡的消息,隻有線程a所屬的對象可以處理。

Android中對Handle機制的了解

子線程傳遞消息給主線程  

publicclass activity2extends activityimplements onclicklistener{  

       button button =null;  

       textview text =null;  

       myhandler mhandler = null;  

       thread thread ;  

       @override  

       protectedvoid 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);  

       }  

       publicvoid onclick(view v) {  

              switch (v.getid()) {  

              case r.id.btn:  

                     thread =new mythread();  

                     thread.start();  

                     break;  

              }              

       }       

       privateclass myhandlerextends handler{               

              public myhandler(looper looper){  

                     super(looper);  

              }  

              @override  

              publicvoid handlemessage(message msg) {//處理消息  

                     text.settext(msg.obj.tostring());  

       privateclass mythreadextends thread{  

              publicvoid 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會自動替主線程建立message queue。在這個子線程裡并沒有建立message queue。是以,mylooper值為null,而mainlooper則指向主線程裡的looper。于是,執行到:

mhandler = new myhandler (mainlooper);

此mhandler屬于主線程。

   mhandler.sendmessage(m);

就将m消息存入到主線程的message queue裡。mainlooper看到message queue裡有訊息,就會作出處理,于是由主線程執行到mhandler的handlemessage()來處理消息。

3、android線程間通信的message機制 

在android下面也有多線程的概念,在c/c++中,子線程可以是一個函數,一般都是一個帶有循環的函數,來處理某些資料,優先線程隻是一個複雜的運算過程,是以可能不需要while循環,運算完成,函數結束,線程就銷毀。對于那些需要控制的線程,一般我們都是和互斥鎖互相關聯,進而來控制線程的進度,一般我們建立子線程,一種線程是很常見的,那就是帶有消息循環的線程。

消息循環是一個很有用的線程方式,曾經自己用c在linux下面實作一個消息循環的機制,往消息隊列裡添加資料,然後異步的等待消息的傳回。當消息隊列為空的時候就會挂起線程,等待新的消息的加入。這是一個很通用的機制。

在android,這裡的線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個looper,這個事android的新概念。我們的主線程(ui線程)就是一個消息循環的線程。針對這種消息循環的機制,我們引入一個新的機制handle,我們有消息循環,就要往消息循環裡面發送相應的消息,自定義消息一般都會有自己對應的處理,消息的發送和清除,消息的的處理,把這些都封裝在handle裡面,注意handle隻是針對那些有looper的線程,不管是ui線程還是子線程,隻要你有looper,我就可以往你的消息隊列裡面添加東西,并做相應的處理。

但是這裡還有一點,就是隻要是關于ui相關的東西,就不能放在子線程中,因為子線程是不能操作ui的,隻能進行資料、系統等其他非ui的操作。

那麼什麼情況下面我們的子線程才能看做是一個有looper的線程呢?我們如何得到它looper的句柄呢?

looper.mylooper();獲得目前的looper

looper.getmainlooper () 獲得ui線程的lopper

我們看看handle的初始化函數,如果沒有參數,那麼他就預設使用的是目前的looper,如果有looper參數,就是用對應的線程的looper。

如果一個線程中調用looper.prepare(),那麼系統就會自動的為該線程建立一個消息隊列,然後調用 looper.loop();之後就進入了消息循環,這個之後就可以發消息、取消息、和處理消息。這個如何發送消息和如何處理消息可以再其他的線程中通過handle來做,但前提是我們的hanle知道這個子線程的looper,但是你如果不是在子線程運作 looper.mylooper(),一般是得不到子線程的looper的。

Android中對Handle機制的了解

public void run() {  

            synchronized (mlock) {  

                looper.prepare();  

               //do something  

            }  

            looper.loop();  

        }  

是以很多人都是這樣做的:我直接在子線程中建立handle,然後在子線程中發送消息,這樣的話就失去了我們多線程的意義了。

Android中對Handle機制的了解

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 ;放在外面,這樣我們的發消息和處理消息都可以在外面來定義,這樣增加程式代碼的美觀,結構更加清晰。

對如任何的handle,裡面必須要重載一個函數

public void handlemessage(message msg)

這個函數就是我們的消息處理,如何處理,這裡完全取決于你,然後通過 obtainmessage和 sendmessage等來生成和發送消息, removemessages(0)來清除消息隊列。google真是太智慧了,這種架構的産生,我們寫代碼更加輕松了。

有的時候,我們的子線程想去改變ui了,這個時候千萬不要再子線程中去修改,獲得ui線程的looper,然後發送消息即可。

我們看看goole music app的源代碼。

在mediaplaybackactivity.java中,我們可以看一下再oncreate中的有這樣的兩句:

Android中對Handle機制的了解

malbumartworker = new worker("album art worker");  

        malbumarthandler = new albumarthandler(malbumartworker.getlooper());  

很明顯這兩句,是建構了一個子線程。并且這個子線程還是looper的子線程,這裡很牛逼的使用了 malbumartworker.getlooper()這個函數,因為我們知道,我們能夠得到子線程的looper的途徑隻有一個:就是在子線程中調用 looper.mylooper (),并且這個函數還要在我們perpare之後調用才能得到正确的looper,但是他這裡用了一個這樣的什麼東東 getlooper,不知道它是如何實作的?

這裡有一個大概的思路,我們在子線程的的prepare之後調用 mylooper ()這個方法,然後儲存在一個成員變量中,這個getlooper就傳回這個東西,但是這裡會碰到多線程的一個很突出的問題,同步。我們在父線程中調用 malbumartworker.getlooper(),但是想要這個傳回正确的looper就必須要求我們的子線程運作了prepare,但是這個東西實在子線程運作的,我們如何保證呢?

我們看google是如何實作的?

Android中對Handle機制的了解

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();  

                while (mlooper == null) {  

                    try {  

                        mlock.wait();  

                    } catch (interruptedexception ex) {  

                    }  

        public looper getlooper() {  

            return mlooper;  

        public void run() {  

                mlooper = looper.mylooper();  

                mlock.notifyall();  

        public void quit() {  

            mlooper.quit();  

    }  

我們知道,一個線程類的構造函數是在主線程中完成的,是以在我們的 worker的構造函數中我們創佳一個線程,然後讓這個線程運作,這一這個線程的建立是指定一個 runnabl,這裡就是我們的worker本身,在主線程調用 t.start();,這後,我們子線程已經建立,并且開始執行work的run方法。然後下面的代碼很藝術:

Android中對Handle機制的了解

synchronized (mlock) {  

我們開始等待我們的子線程給mlooper指派,如果不指派我們就繼續等,然後我們的子線程在運作run方法之後,在給 mlooper指派之後,通知worker夠着函數中的wait,然後我們的構造函數才能完成,是以我們說:

malbumartworker = new worker("album art worker");

這句本身就是阻塞的,它建立了一個子線程,開啟了子線程,并且等待子線程給mlooper指派,指派完成之後,這個函數才傳回,這樣才能保證我們的子線程的looper的擷取絕對是正确的,這個構思很有創意。值得借鑒<!--++ plugin_code qcomic begin--> <!--++ plugin_code qcomic end--> 

4、android中handler的使用方法-在子線程中更新界面 

http://blog.csdn.net/chaoyu168/article/details/50914021

二、知識要點 

一、知識點 

1、handler應該由處理消息的線程建立。 

2、handler與建立它的線程相關聯,而且也隻與建立它的線程相關聯。handler運作在建立它的線程中,是以,如果在handler中進行耗時的操作,會阻塞建立它的線程。

【來源】以上來自: 

二、一些知識點 

1、android的線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個looper。主線程(ui線程)就是一個消息循環的線程。

2、 

looper.mylooper();      //獲得目前的looper 

looper.getmainlooper () //獲得ui線程的lopper 

3、handle的初始化函數(構造函數),如果沒有參數,那麼他就預設使用的是目前的looper,如果有looper參數,就是用對應的線程的looper。

三、應用執行個體 

3.1 handler傳遞message【應用示例一】 

Android中對Handle機制的了解

package com.android.tutor;    

import java.util.timer;    

import java.util.timertask;    

import android.app.activity;    

import android.os.bundle;    

import android.os.handler;    

import android.os.message;    

public class handlerdemo extends activity {    

    //title為settitle方法提供變量,這裡為了友善我設定成了int型    

    private int title = 0;    

    private handler mhandler = new handler(){             

        public void handlemessage(message msg) {    

            switch (msg.what) {    

            case 1:    

                updatetitle();    

                break;    

            }    

        };    

    };    

    public void oncreate(bundle savedinstancestate) {    

        super.oncreate(savedinstancestate);    

        setcontentview(r.layout.main);    

        timer timer = new timer();    

        timer.scheduleatfixedrate(new mytask(), 1, 5000);    

    }    

    private class mytask extends timertask{    

        @override    

        public void run() {    

            message message = new message();    

            message.what = 1;    

           mhandler.sendmessage(message);                 

        }       

    public void updatetitle(){    

        settitle("welcome to mr wei's blog " + title);    

        title ++;    

}    

上面的代碼,直接在主線程中定義handler成員。在子線程中通過主線程的handler向主線程發送消息。其使用步驟如下: 

1、在主線程中定義handler,并為這個handler實作handlemessage方法。 

2、在子線程中調用主線程的handler,通過其sendmessage方法發送消息。 

3.2 handler傳遞runnable對象還有另外一種用handler來進行線程間通信的方式,那就是用handler來傳遞一個runnable對象,而不是一個message。

【應用執行個體三】 

使用步驟 

1、在主線程中定義handler對象 

2、構造一個runnable對象,為該對象實作runnable方法,在該方法中進行一些你想做的耗時操作。 

3、在子線程中使用handler對象post(runnable)對象.

轉載:http://blog.csdn.net/chaoyu168/article/details/50913941