1. 程式人生 > >Android中對Handle機制的理解

Android中對Handle機制的理解

trac 意義 還要 break create findview curl net protected

一、重要參考資料
【參考資料】
眼下來看,以下的幾個網址中的內容質量比較不錯。基本不須要再讀別的網址了。


1、android消息機制一
http://xtfncel.javaeye.com/blog/663517
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所送出的消息。

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

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
              }             
       } 
} 

說明:
此程序啟動時,當前線程(即主線程, main thread)已誕生了一個Looper對象。而且有了一個MessageQueue數據結構。
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!"於畫面上。



2、Android消息機制二
http://xtfncel.javaeye.com/blog/663518

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所屬的對象能夠處理。

子線程傳遞消息給主線程

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{

              @Override

              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機制
http://www.javafun.cn/viewthread.php?tid=15174、

在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的。

public void run() {
            synchronized (mLock) {
                Looper.prepare();
               //do something
            }
            Looper.loop();
        }
所以非常多人都是這樣做的:我直接在子線程中新建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中的有這種兩句:

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是怎樣實現的?

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 protected]][email protected][/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();
        }
    }
我們知道。一個線程類的構造函數是在主線程中完畢的。所以在我們的 Worker的構造函數中我們創佳一個線程。然後讓這個線程執行,這一這個線程的創建是指定一個 Runnabl,這裏就是我們的Worker本身,在主線程調用 t.start();,這後,我們子線程已經創建,而且開始運行work的run方法。然後以下的代碼非常藝術:

synchronized (mLock) {
                while (mLooper == null) {
                    try {
                        mLock.wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }

我們開始等待我們的子線程給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。

4、假設一個線程中調用Looper.prepare(),那麽系統就會自己主動的為該線程建立一個消息隊列。然後調用 Looper.loop();之後就進入了消息循環。這個之後就能夠發消息、取消息、和處理消息。【來源】以上來自:http://www.javafun.cn/viewthread.php?tid=1517

三、應用實例
3.1 handler傳遞message【應用演示樣例一】

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)對象.



Android中對Handle機制的理解