Android線程間通訊機制

來源:互聯網
上載者:User

標籤:

當android應用程式運行時,一個主線程被建立(也稱作UI線程),此線程主要負責處理UI相關的事件,由於Android採用UI單執行緒模式,所以只能在主線程中對UI元素進行操作,如果在非UI線程直接對UI進行了操作,則會報錯,另外,對於運算量較大的操作和IO操作,我們需要新開線程來處理這些工作,以免阻塞UI線程,子線程與主線程之間是怎樣進行通訊的呢?此時就要採用訊息迴圈機制(Looper)與Handler進行處理。

一、基本概念

Looper:每一個線程都可以產生一個Looper,用來管理線程的Message,Looper對象會建立一個MessgaeQueue資料結構來存放message。

Handler:與Looper溝通的對象,可以push訊息或者runnable對象到MessgaeQueue,也可以從MessageQueue得到訊息。

查看其建構函式:

Handler()

Default constructor associates this handler with the queue for the current thread.//如不指定Looper參數則預設利用當前線程的Looper建立

Handler(Looper looper)

Use the provided queue instead of the default one.//使用指定的Looper對象建立Handler

線程A的Handler對象引用可以傳遞給別的線程,讓別的線程B或C等能送訊息來給線程A。

線程A的Message Queue裡的訊息,只有線程A所屬的對象可以處理。

注意:Android裡沒有global的MessageQueue,不同進程(或APK之間)不能通過MessageQueue交換訊息。


二、Handler通過Message通訊的基本方式

使用Looper.myLooper可以取得當前線程的Looper對象。

使用mHandler = new Handler(Looper.myLooper()); 可產生用來處理當前線程的Handler對象。

使用mHandler = new Handler(Looper.getMainLooper()); 可誕生用來處理main線程的Handler對象。

使用Handler傳遞訊息對象時將訊息封裝到一個Message對象中,Message對象中主要欄位如下:

Message對象可以通過Message類的建構函式獲得,但Google推薦使用Message.obtain()方法獲得,該方法會從全域的對象池裡返回一個可複用的Messgae執行個體,API中解釋如下:

Message()

Constructor (but the preferred way to get a Message is to call Message.obtain()).

Handler發出訊息時,既可以指定訊息被接受後馬上處理,也可以指定經過一定時間間隔之後被處理,如sendMessageDelayed(Message msg, long delayMillis),具體請參考API。

Handler訊息被發送出去之後,將由handleMessage(Message msg)方法處理。

注意:在Android裡,新誕生一個線程,並不會自動建立其Message Loop可以通過調用Looper.prepare()為該線程建立一個MessageQueue,再調用Looper.loop()進行訊息迴圈。

下面舉例說明:

在main.xml中定義兩個button及一個textview

public class HandlerTestActivity extends Activity implements OnClickListener{    public TextView tv;    private myThread myT;    Button bt1, bt2;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        bt1 = (Button)findViewById(R.id.a);        bt2 = (Button)findViewById(R.id.b);        tv = (TextView)findViewById(R.id.tv);        bt1.setId(1);//為兩個button設定ID,此ID用於後面判斷是哪個button被按下        bt2.setId(2);        bt1.setOnClickListener(this);//增加監聽器        bt2.setOnClickListener(this);    }    @Override    public void onClick(View v) {       switch(v.getId()){//按鍵事件響應,如果是第一個按鍵將啟動一個新線程       case 1:            myT = new myThread();            myT.start();            break;       case 2:            finish();            break;       default:            break;       }    }      class myThread extends Thread {        private EHandler mHandler;        public void run() {             Looper myLooper, mainLooper;             myLooper = Looper.myLooper();//得到當前線程的Looper             mainLooper = Looper.getMainLooper();//得到UI線程的Looper             String obj;             if(myLooper == null) { //判斷當前線程是否有訊息迴圈Looper                  mHandler = new EHandler(mainLooper);                  obj = "current thread has no looper!";//當前Looper為空白,EHandler用mainLooper物件建構             } else {              mHandler = new EHandler(myLooper);//當前Looper不為空白,EHandler用當前線程的Looper物件建構              obj = "This is from current thread.";             }             mHandler.removeMessages(0);//清空訊息佇列裡的內容             Message m = mHandler.obtainMessage(1, 1, 1, obj);             mHandler.sendMessage(m);//發送訊息           }    }    class EHandler extends Handler {        public EHandler(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) { //訊息處理函數           tv.setText((String)msg.obj);//設定TextView內容        }    }}

程式運行後點擊TestLooper按鍵,TextView輸出如下,說明新建立的線程裡Looper為空白,也就說明了新建立的線程並不會自己建立Message Looper。

修改myThread類:

class myThread extends Thread {    private EHandler mHandler;    public void run() {        Looper myLooper, mainLooper;        Looper.prepare();        myLooper = Looper.myLooper();        mainLooper = Looper.getMainLooper();        ......        ......        mHandler.sendMessage(m);        Looper.loop();    }}

Looper.prepare為當前線程建立一個Message Looper,Looper.loop()開啟訊息迴圈。這樣修改是OK呢?

答案是否定的!運行時Logcat將拋出CalledFromWrongThreadException異常錯誤,提示如下:

意思就是說“只有原始建立這個視圖層次的線程才能修改它的視圖”,本例中的TextView是在UI線程(主線程)中建立,因此,myThread線程不能修改其顯示內容!

一般的做法是在子線程裡擷取主線程裡的Handler對象,然後通過該對象向主線程的訊息佇列裡發送訊息,進行通訊。

如果子線程想修改主線程的UI,可以通過發送Message給主線程的訊息佇列,主線程經行判斷處理再對UI經行操作,具體可以參考之前的代碼。


三、Handler通過runnable通訊的基本方式

我們可以通過Handler的post方法實現線程間的通訊,API中關於post方法說明如下

public final boolean post (Runnable r)

Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

Post方法將安排runnable對象在主線程的某個位置運行,但是並不會開啟一個新的線程,驗證代碼如下:

public class HandlerTestActivity extends Activity {    private Handler handlerTest;    Runnable runnableTest = new Runnable() {       public void run() {           String runID = String.valueOf(Thread.currentThread().getId());//輸出Runnable 線程的ID號           Log.v("Debug",runID);       }    };    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        handlerTest = new Handler();        String mainID = String.valueOf(Thread.currentThread().getId());        Log.v("Debug",mainID);//輸出主線程的ID號        handlerTest.post(runnableTest);    }}

Logcat裡輸出如下:

說明只是把runnable裡的run方法放到UI線程裡運行,並不會建立新線程

因此我們可以在子線程中將runnable加入到主線程的MessageQueue,然後主線程將調用runnable的方法,可以在此方法中更新主線程UI。

將之前的代碼修改如下:

public class HandlerTestActivity extends Activity {    private Handler handlerTest;    private TextView tv;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        tv = (TextView)findViewById(R.id.tv);//TextView初始化為空白        handlerTest = new Handler();        myThread myT = new myThread();        myT.start();//開啟子線程    }    class myThread extends Thread{        public void run(){            handlerTest.post(runnableTest);//子線程將runnable加入訊息佇列        }    }    Runnable runnableTest = new Runnable() {       public void run() {           tv.setText("此資訊由子線程輸出!");       }    };}

相當於在主線程中調用了runnalbe的run方法,更改了TextView的UI!

Android線程間通訊機制

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.