Android中的訊息處理執行個體與分析

來源:互聯網
上載者:User

Android中的訊息處理執行個體與分析
Android中的訊息處理執行個體與分析摘要

本文介紹了Android中的訊息處理機制,給出了Android訊息處理中的幾個重點類Handler、Message、MessageQueue、Looper、Runnable、Thread的詳細介紹,提供了兩個訊息處理的執行個體代碼,並深入分析了使用Android訊息機制應該遵循的幾個原則。

閱讀本文的收穫

在具有java基礎的情況下,Android的學習比較輕鬆,很多人在沒有深刻瞭解Android訊息處理機制的背景下,已經能夠開發出可用的app,很多人開始想學習Android訊息處理機制的第一個動機是遇到了一個著名的bug“CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.”。這個bug的含義即“只有建立一個view層次的線程能夠更新此view”,在更多情況下,它是想說“只有主線程能夠更新UI”。
本文即是來解釋如何利用Android的訊息機制從主線程或者子線程中更新UI或完成其他動作。你可以學到:
1. 如何使用Android的訊息實現同步、非同步作業;
2. 如何在主線程和子線程發送並接收訊息;
3. 訊息是如何得到處理的;
4. 使用Android的訊息處理機制應該遵循的幾個原則;
5. 兩個具體的訊息處理執行個體原始碼。

閱讀本文需要的技術背景

你可能需要如下技術背景才能完全理解本文的內容,如何你沒有以下背景,建議先學習相關知識:
1. Java語言基礎
2. Java多線程技術
3. Android開發基本知識

第一個例子:在主線程和子線程中發送訊息,在主線程中處理訊息

先來看一個代碼,代碼地址為:
http://download.csdn.net/detail/logicteamleader/8827099
本例的作用是:建立一個Handler(處理訊息的類),在介面上點擊按鈕就會向此Handler發送訊息,Handler可以處理這些訊息(後面會詳細解釋,訊息的處理其實是多個類共同合作的結果),對UI進行修改。介面上有八個按鈕,從上至下其效果分別是:
1. 使用Handler的post來傳遞一個Runnable的執行個體,該執行個體的run方法會在按鈕點擊時運行;
2. 使用Handler的postDelayed(Runnable r, long delayMillis)來傳遞一個Runnable的執行個體,該執行個體的run方法會在一段時間delayMillis後執行;
3. 使用Handler的sendMessage方法傳遞一個訊息,該訊息在Handler的handleMessage方法中被解析執行;
4. 使用Message的sendToTarget方法向獲得該Message的Handler傳遞一個訊息,該訊息在Handler的handleMessage方法中被解析執行;
第5、6、7、8個方法與上面四個方法類似,不同的是它們都是在子線程中調用的。
原始碼如下:

package com.example.wxbhandlertest;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.os.MessageQueue;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;/** * @author wxb * Android中的訊息處理執行個體之一 * * 一、在主線程內發送訊息 * 1.使用post * 2.使用postDelay * 3.使用sendMessage * 4.使用Message.sentToTarget * 二、在子線程中使用Handler * 1.使用post * 2.使用postDelay * 3.使用sendMessage * 4.使用Message.sentToTarget */public class MainActivity extends Activity {    private Runnable runnable=null;    private Runnable runnableDelay=null;    private Runnable runnableInThread=null;    private Runnable runnableDelayInThread=null;    private static TextView tv;    private static TextView tvOnOtherThread;    //自訂Message類型    public final static int MESSAGE_WXB_1 = 1;    public final static int MESSAGE_WXB_2 = 2;    public final static int MESSAGE_WXB_3 = 3;    public final static int MESSAGE_WXB_4 = 4;    public final static int MESSAGE_WXB_5 = 5;    private static Handler mHandler=new Handler(){        @Override        public void handleMessage(Message msg) {            switch(msg.what){            case MESSAGE_WXB_1:                tv.setText("invoke sendMessage in main thread");                break;            case MESSAGE_WXB_2:                tv.setText("Message.sendToTarget in main thread");                break;            case MESSAGE_WXB_3:                tvOnOtherThread.setText("invoke sendMessage in other thread");                break;            case MESSAGE_WXB_4:                tvOnOtherThread.setText("Message.sendToTarget in other thread");                break;              }            super.handleMessage(msg);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv = (TextView) this.findViewById(R.id.tvOnMainThread);        tvOnOtherThread = (TextView) this.findViewById(R.id.tvOnOtherThread);        //方法1.post        runnable = new Runnable(){            public void run(){                tv.setText(getString(R.string.postRunnable));            }        };        Button handler_post = (Button) this.findViewById(R.id.btnHandlerpost);        handler_post.setOnClickListener(new OnClickListener() {                 @Override            public void onClick(View v) {                mHandler.post(runnable);            }        });        //方法2:postDelay        runnableDelay = new Runnable(){            public void run(){                tv.setText(getString(R.string.postRunnableDelay));            }        };        Button handler_post_delay = (Button) this.findViewById(R.id.btnHandlerPostdelay);        handler_post_delay.setOnClickListener(new OnClickListener() {                   @Override            public void onClick(View v) {                mHandler.postDelayed(runnableDelay, 1000);  //1秒後執行            }        });        //方法3:sendMessage        Button btnSendMessage = (Button) this.findViewById(R.id.btnSendMessage);        btnSendMessage.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Message msg = mHandler.obtainMessage();                msg.what = MESSAGE_WXB_1;                mHandler.sendMessage(msg);            }        });        //方法4:Message.sendToTarget        Button btnSendtoTarget = (Button) this.findViewById(R.id.btnSendtoTarget);        btnSendtoTarget.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Message msg = mHandler.obtainMessage();                msg.what = MESSAGE_WXB_2;                msg.sendToTarget();            }        });       //在其他線程中發送訊息        //1.post        runnableInThread = new Runnable(){            public void run(){                tvOnOtherThread.setText(getString(R.string.postRunnableInThread));            }        };        Button btnPost = (Button) this.findViewById(R.id.btnPost);        btnPost.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        mHandler.post(runnableInThread);                    }                }.start();            }        });        //2.postDelay        runnableDelayInThread = new Runnable(){            public void run(){                tvOnOtherThread.setText(getString(R.string.postRunnableDelayInThread));            }        };        Button btnPostDelay = (Button) this.findViewById(R.id.btnPostDelay);        btnPostDelay.setOnClickListener(new OnClickListener() {                         @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        mHandler.postDelayed(runnableDelayInThread, 1000);                    }                }.start();            }        });        //3.sendMessage        Button btnSendMessage2 = (Button) this.findViewById(R.id.btnSendMessage2);        btnSendMessage2.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        Message msg = mHandler.obtainMessage();                        msg.what = MESSAGE_WXB_3;                        mHandler.sendMessage(msg);                    }                }.start();            }        });      //方法4:Message.sendToTarget        Button btnSendToTarget2 = (Button) this.findViewById(R.id.btnSendToTarget2);        btnSendToTarget2.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                new Thread(){                    @Override                    public void run() {                        super.run();                        mHandler.obtainMessage(MESSAGE_WXB_4).sendToTarget();                    }                }.start();            }        });    }}
幾個規則

看完代碼後,在解釋具體類之前,先瞭解幾個規則:
第一, 只有建立view的線程能夠更新此view;一般來說,建立UI的是Android主線程,因此只有在主線程中才能更新UI;
第二, 處理訊息的類是Handler,它依附於建立自己的線程;如果在主線程中建立Handler mHandler,則向mHandler發送的訊息會在主線程中被解析;如果在子線程中建立Handler sHandler,則向sHandler發送的訊息會在子線程中被解析;
第三, 發送訊息有三類方法,Handler的post方法,Handler的sendMessage方法,以及Message的sentToTarget方法,它們最終其實都調用了Handler的sendMessage方法;
第四, 訊息的方法可以即時也可以延時,代表就是post和postDelay,以及sendMessage和sendMessageDelayed;延時發送訊息可以實現很多使用者需要的介面延時效果,例如最常用的SplashWindow。
第五, post類型的方法只需要執行個體化一個Runnable介面,且不需要重載Handler. handleMessage方法,比較簡單易用;
第六, sendMessage和sentToTarget需要重載Handler. handleMessage方法,實現對不同訊息的解析。

Handler

Handler是Android訊息處理中最重要的一個類,不管什麼程式設計語言或者架構,叫Handler的類一般都很重要,也都很複雜,當年的Windows控制代碼類更是讓人一頭霧水。
幸好,Android的Handler很容易理解和使用。它就是一個訊息處理的目標類,其主要作用有兩個:1)它安排要執行的訊息和runnable在未來某個時間點執行;2)處理來自不同線程的訊息並執行操作。
使用Handler要注意以下幾點:
1. Handler屬於建立它的線程,並與線程的訊息迴圈關聯,同時與此線程的訊息佇列(MessageQueue)關聯。
2. Handler發送訊息的方法有兩類:post類和sendMessage類,用法見例子;
3. 如果使用post發送訊息,則訊息中包含的Runnable會在解析訊息時執行;
4. 如果使用sendMessage發送訊息,則需要重載Handler. handleMessage方法,實現對不同訊息的解析

Message

Message是訊息類,它有幾個重要的屬性:
public int what:訊息類型
public int arg1:參數1
public int arg2:參數2
public Object obj:對象參數
以上幾個參數使用者可以隨意定製。
值得注意的有幾點:
1. Android使用訊息池模式來提高訊息的效率,因此一般不適用new來建立訊息,而是使用obtain方法來從訊息池中擷取一個Message的執行個體;Handler類也有obtain方法;
2. Message有一個Handler作為Target,一般來說就是擷取該訊息的所線上程的關聯Handler;因此使用sendToTarget方法發送訊息時,將訊息發給它的Target;

MessageQueue

MessageQueue是訊息佇列,一個線程最多擁有一個訊息佇列,這個類一般不會被程式員直接使用。它的入隊方法enqueueMessage用來將一個Message排入佇列,這個方法是安全執行緒的,因此才保證了Android的訊息處理機制是安全執行緒的。
MessageQueue的next方法用來擷取下一個Message,沒有任何訊息時,主線程常常在此方法處等待。

Runnable

Java的介面,它代表一個可執行檔程式碼片段,如下所示:

public interface Runnable {    /**     * Starts executing the active part of the class' code. This method is     * called when a thread is started that has been created with a class which     * implements {@code Runnable}.     */    public void run();}
Thread

Java的線程類,大家都應該很熟悉了。

第二個例子:在子線程中建立訊息處理迴圈

第一個例子描述了如何在多個線程中發送訊息,並在主線程中統一接收和處理這些訊息;第二個例子則描述如何在子線程中建立一個訊息迴圈,並從主線程發送訊息給子線程,讓子線程處理這些訊息。
第二個例子中有兩個訊息迴圈,兩個Handler,主線程首先向子線程發送一個訊息,子線程的收到訊息後再向主線程發送一個訊息,主線程收到訊息後更新UI。
例子代碼如下:
http://download.csdn.net/detail/logicteamleader/8827401
原始碼如下:

package com.example.wxbloopinthread;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity {    private static TextView tv = null;    //自訂Message類型    public final static int MESSAGE_WXB_1 = 1;    //主線程中建立Handler    private static Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch(msg.what){            case MESSAGE_WXB_1:                tv.setText("主線程發送,子線程接收訊息後回傳,主線程修改UI");                break;            }            super.handleMessage(msg);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);             setContentView(R.layout.activity_main);        tv = (TextView) this.findViewById(R.id.textView1);        //建立子線程        new LooperThread().start();        //點擊按鈕向子線程發送訊息        Button btn = (Button) this.findViewById(R.id.btnSendMessage);        btn.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                LooperThread.sHandler.sendEmptyMessage(MESSAGE_WXB_1);            }        });    }    //定義子線程    static class LooperThread extends Thread {        public static Handler sHandler = null;        public void run() {            //建立訊息迴圈            Looper.prepare();            sHandler = new Handler() {                public void handleMessage(Message msg) {                    switch(msg.what){                    case MESSAGE_WXB_1:                        mHandler.sendEmptyMessage(MESSAGE_WXB_1);                        break;                    }                }            };            //開啟訊息迴圈            Looper.loop();        }    }}

第二個例子使用了另一個重要的類Looper,它是代表Android中的訊息迴圈處理類。

Looper

Looper被用來建立一個線程的訊息迴圈。線程預設情況下是沒有訊息迴圈的,要建立訊息迴圈,必須先調用Looper.prepare,然後在合適的地方調用Looper.loop,這個loop方法就開始迴圈處理本線程接收到的訊息,直到loop迴圈被停止。
在大部分情況下,Looper都是和Handler一起使用,它通過Handler接收和處理訊息。
使用Looper要注意以下幾點:
1. Android的主線程是預設已經建立了Looper對象的,因此不能在主線程中調用Looper.prepare;
2. Looper.prepare和Looper.loop都是靜態方法,調用時要注意,不要使用new來建立一個Looper;

總結

使用Android訊息的方法有以下幾種:
1. 使用Handler和Runnable,即時或延時發送一個訊息;
2. 使用Handler和Message,即時或延時發送一個訊息,需重載Handler. handleMessage方法;
需要注意的規則有以下幾條:
1. 只有建立view的線程能夠更新此view;一般來說,建立UI的是Android主線程,因此只有在主線程中才能更新UI;
2. 處理訊息的類是Handler,它依附於建立自己的線程;如果在主線程中建立Handler mHandler,則向mHandler發送的訊息會在主線程中被解析;如果在子線程中建立Handler sHandler,則向sHandler發送的訊息會在子線程中被解析;
3. Looper被用來建立一個線程的訊息迴圈,要建立訊息迴圈,必須先調用Looper.prepare,然後在合適的地方調用Looper.loop。
具體的體會,還是要多看代碼才行。

聯繫我們

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