Android Handler Message Looper機制原理

來源:互聯網
上載者:User

  在Android中談到Handler,我們首先來講一下Handler的機制和原理

     1、Handler機制原理

 

        Handler:主要是用來處理髮送和接收訊息的,作用是把訊息加入特定的(Looper)訊息佇列中,並分發和處理該訊息佇列中的訊息。構造Handler的時候可以指定一個Looper對象,通過Handler對象我們可以封裝Message對象,然後通過sendMessage(msg)把Message對象添加MessageQueue中;當MessageQueue迴圈到該Message時,就會調用該Message對象對應的handler對象的handleMessage()方法對其進行處理。

     在主線程通過Handler handler  = new Handler();即使用預設建構函式構造Handler時,是預設使用主線程的Looper對象,通過這種方式構造的Handler是屬於主線程的,也就是Handler和Looper是綁定在一起的,如果構造Handler時指定一個新線程的Looper對象,則該Handler對象是屬於該子線程的。

     每個Android應用程式都運行在一個dalvik虛擬機器進程中,進程開始的時候會啟動一個主線程(MainThread),主線程負責處理和ui相關的事件,因此主線程通常又叫UI線程。而由於Android採用UI單執行緒模式,所以只能在主線程中對UI元素進行操作。如果在非UI線程直接對UI進行了操作,則會報錯,Handler是需要在主線程中進行初始化的。為什麼要這麼說呢,因為你在自己new一個新線程中去簡單建立一個Handler,程式執行是會報錯的:

 

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()    atandroid.os.Handler.<init>(Handler.java:121)at com.cao.android.demos.handles.HandleTestActivity$MyThread$1.<init>(HandleTestActivity.java:86)     at com.cao.android.demos.handles.HandleTestActivity$MyThread.run(HandleTestActivity.java:86

    為什麼在主線程中不會報錯,而在自己建立的線程中就會報這個錯誤呢?很簡單,因為主線程它已經建立了Looper,因為主線程預設有一個自己的Looper對象和Message對象,非主線程預設是沒有自己的Looper,需要自己去建立:通過Looper.prepare建立一個Looper對象,以及
Looper.loop()建立訊息迴圈隊列;在新線程(非主線程)中建立Handler對象,有兩種方法實現:

(1)建立Handler之前通過 Looper.prepare()方法建立Looper對象

class MyThread extends Thread {   public void run() {           Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread .currentThread().getName()));       //非主線程中建立一個handler       Looper.prepare();// 建立該線程的Looper對象,用於接收訊息,在非主線程中是沒有looper的所以在建立handler前一定要使用prepare()建立一個Looper       Handler myThreadHandler = new Handler() {          public void handleMessage(android.os.Message msg) {       Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler handleMessage run...",       Thread .currentThread().getName()));                                }        Looper.myLooper().loop();//建立一個訊息迴圈,該線程不會退出                             };  } }

 

(2)使用主線程的Looper對象,因為每個主線程預設已經建立了自己的Looper對象

class MyThread extends Thread {   public void run() {           Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread .currentThread().getName()));       Handler threadMainLoopHandler = new Handler(Looper.getMainLooper()) {            //該handleMessage方法將在mainthread中執行              public void handleMessage(android.os.Message msg) {           Log.d(Constant.Tag,MessageFormat.format("Thread[{0}]--threadMainLoopHandler handleMessage run...",    Thread.currentThread().getName()));                                             }          };                 } }

(3)小結

  (1)和(2)的最主要區別在於:(1)的Handler對象屬於子線程,其handleMessage是在子線程中執行,而(2)的Handler是屬於主線程的,因為它和主線程的Looper.getMainLooper()) 對象綁定在一起的,所以在handleMessage方法中不要做複雜耗時的操作如網路、IO操作等,否則會阻塞UI線程,造成ANR現象。
 

    熟悉了Handler、Looper機制的原理之後,接下來對於如何通過Handler來實現非同步處理就更容易理解了,首先我們看一個通過Handler和Runnable來實現一個類似的計算機功能:

2、Handler+runable(可以實作類別似一個計數器的功能,注意與下面3的區別,是同步還是非同步方式呢?)

      Handler對象在進行初始化的時候,會預設的自動綁定訊息佇列。利用類post方法,可以將Runnable對象發送到訊息佇列中,按照隊列的機制按順序執行不同的Runnable對象中的run方法。

1.public class HandlerActivity extends Activity {  2.    /** Called when the activity is first created. */ 3.    //聲明兩個按鈕控制項  4.    private Button startButton = null;  5.    private Button endButton = null;  6.    @Override 7.    public void onCreate(Bundle savedInstanceState) {  8.        super.onCreate(savedInstanceState);  9.        setContentView(R.layout.main);  10.        //根據控制項的ID得到代表控制項的對象,並未這兩個按鈕設定相應的監聽器  11.        startButton = (Button)findViewById(R.id.startButton);  12.        startButton.setOnClickListener(new StartButtonListener());  13.        endButton = (Button)findViewById(R.id.endButton);  14.        endButton.setOnClickListener(new EndButtonListener());  15.          16.    }  17.    class StartButtonListener implements OnClickListener{  18. 19.        @Override 20.        public void onClick(View v) {  21.            //調用Handler的post方法,將要執行的線程對象添加到隊列當中  22.            handler.post(updateThread);  23.        }  24.          25.    }  26.      27.    class EndButtonListener implements OnClickListener{  28. 29.        @Override 30.        public void onClick(View v) {  31.            handler.removeCallbacks(updateThread);  32.        }  33.          34.    }  35.    //建立一個Handler對象  36.    Handler handler  = new Handler();  37.    //將要執行的操作寫線上程對象的run方法當中  38.    Runnable updateThread =  new Runnable(){  39. 40.        @Override 41.        public void run() {  42.            System.out.println("UpdateThread");  43.            //在run方法內部,執行postDelayed或者是post方法  44.            handler.postDelayed(updateThread, 3000);  45.        }  46.          47.    };  48.} 
 3、通過Handler來實現非同步處理的2種方式   3.1  採用Thread+ handler Message 方式

 

      Thread+ handler Message 方式 即 :線程開啟,採用Message傳遞後台(子)線程和UI主線程之間的資訊,UI線程收到資訊之後,進行更新UI的操作。 

 

public class handlerTestActivity extends Activity{    private ProgressBar bar = null;    private boolean isRunning = false;        /* 我們為這個Acivity建立一個用於和背景程式通訊的handler,簡單地,只要一收到message,就將progressbar進度增加5。*/    /* 步驟1:建立Handler,並通過handleMessage()給出當收到訊息是UI需要進行如何處理,例子簡單不對msg的內容進行分析*/    Handler handler= new Handler(){        public void handleMessage(Message msg) {            bar.incrementProgressBy(5);        }    };        protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.chapter_15_test1);        bar=(ProgressBar)findViewById(R.id.c15_progress);    }    /*on Start是UI初始化並顯示時調用*/    protected void onStart() {        super.onStart();        bar.setProgress(0);        /*步驟2:建立後台線程處理,採用Thread,其中run()的內容,就是線程平行處理的內容,Thread是Runnable的implements*/        Thread background = new Thread(new Runnable(){            public void run() {                try{                    for(int i = 0; i < 20 && isRunning; i ++){                        Thread.sleep(1000);                        /* 步驟2.1:發送Message到隊列中,參數中的obtainMessage()是用於給出一個新Message,本例無參數,對應的在handler在隊列中收到這條訊息時,則通過handleMessage()進行處理*/                        handler.sendMessage(handler.obtainMessage());                    }                }catch(Throwable t){                    //jest end the thread                }            }                    });        isRunning = true;       /*步驟3:啟動線程*/        background.start();    }    /*onStop是UI停止顯示時調用,例如我們按了返回鍵*/    protected void onStop() {        super.onStop();        isRunning = false;    }  }
3.2  Handler 和多執行緒方式,採用 Handler Post 、Runnable來實現(注意和2的區別)

      Handler Post方法雖然發送的是一個實現了Runnable介面的類對象,但是它並非建立了一個新線程,而是執行了該對象中的run方法。也就是說,整個run中的操作和主線程處於同一個線程。

     這樣對於那些簡單的操作,似乎並不會影響。但是對於耗時較長的操作,當它被加入到訊息佇列中之後執行會佔用很長的時間,以至於處於同一線程的其他動作無法繼續執行,就會出現“假死”。為瞭解決這個問題,就需要使得handler綁定到一個新開啟線程的訊息佇列上,在這個處於另外線程的上的訊息佇列中處理傳過來的Runnable對象和訊息。SDK文檔中也提供了相關說明:

  

1.public class HandlerTest2 extends Activity {  2. 3.    @Override 4.    protected void onCreate(Bundle savedInstanceState) {  5.        // TODO Auto-generated method stub  6.    super.onCreate(savedInstanceState);  7.    setContentView(R.layout.main);  8.    //列印了當前線程的ID  9.    System.out.println("Activity-->" + Thread.currentThread().getId());  10.11.    //產生一個HandlerThread對象  12.    HandlerThread handlerThread = new HandlerThread("handler_thread");  13.    //在使用HandlerThread的getLooper()方法之前,必須先調用該類的start(),同時開啟一個新線程;  14.    handlerThread.start();15.    //將由HandlerThread擷取的Looper傳遞給Handler對象,即由處於另外線程的Looper代替handler初始化時預設綁定的訊息佇列來處理訊息。  16.   //自訂Handler17.    MyHandler myHandler = new MyHandler(handlerThread.getLooper());  18.    Message msg = myHandler.obtainMessage();  19.    //將msg發送到目標對象,所謂的目標對象,就是產生該msg對象的handler對象  20.    Bundle b = new Bundle();  21.    b.putInt("age", 20);  22.    b.putString("name", "Jhon");  23.    msg.setData(b);  24.    msg.sendToTarget();//將msg發送到myHandler25.    }  26.      27.    //自訂Handler類28.    class MyHandler extends Handler{  29.    public MyHandler(){  30.              31.    }  32.    33.    public MyHandler(Looper looper){  34.        super(looper);  35.    }  36.    //這裡的handleMessage將在子線程handlerThread中執行而不是在主線程中執行37.    @Override 38.    public void handleMessage(Message msg) {  39.        Bundle b = msg.getData();  40.        int age = b.getInt("age");  41.      String name = b.getString("name");  42.     System.out.println("age is " + age + ", name is" + name);  43.System.out.println("Handler--->" + Thread.currentThread().getId());          System.out.println("handlerMessage");  44.        }  45.    }  46.}  

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.