【Android 開發】: Android 訊息處理機制之一: Handler 與 Message
現在我們就來學習一下Android的訊息處理,以及剖析一下相關類如Handler和Message類的原始碼,同時使用他們來更新UI主執行緒的操作。因為Android的訊息處理機制內容繁多,我們分為幾部分來學習,大家可以關注這幾講內容,這一講我們重點來學習一下Handler和Message.
一. Handler類介紹
1). 檢視Android官網API Handler類
java.lang.Object
android.os.Handler
Known Direct Subclasses
AsyncQueryHandler, AsyncQueryHandler.WorkerHandler, HttpAuthHandler, SslErrorHandler
一個Handler會允許你傳送和處理Message或者Runnable物件關聯到一個執行緒的訊息佇列MessageQueue中,每一個Handler的例項都會關聯一個單一的執行緒和那個執行緒的訊息佇列中。當你建立一個一個新的Handler,它會繫結到你建立的執行緒和這個執行緒訊息佇列中。並且指向好它,它會讓訊息傳遞到關聯好它的訊息佇列中,當它從訊息隊列出隊的時候執行它。這裡他們的如何關聯的不是很懂!
對於Handler來說有兩種主要的方式: 1. 計劃好訊息和Runnable將來的某一個時間點來執行它 2. 從一個不同的執行緒中執行Handler的入隊操作。分發訊息由下面的幾個方法完成:
1) post(Runnable),
2) postAtTime(Runnable, long),
3) postDelayed(Runnable, long),
4) sendEmptyMessage(int),
5) sendMessage(Message),
6) sendMessageAtTime(Message, long),
7) sendMessageDelayed(Message, long)post方式的方法可以將一個Runable物件排列到訊息佇列中。sendMessage方式的方法可以通過 Handler的handleMessage(Message) 方法攜帶有bundle型別的資料的Message物件到佇列中(需要你實現Handler的子類)。你可以通過上訴兩種方式來出來Handler,你可以允許你的訊息在訊息佇列中準備好就馬上被處理,也可以處理之前指定一些延時讓你實現超時或者基於時間的行為。
當你的應用程式的程序被建立的時候,它的主執行緒專門用來處理正常執行的主執行緒的訊息佇列,(也就是說UI主執行緒有自己的訊息佇列,所以我們沒必要在UI主執行緒中處理自己的訊息)它關心的是管理頂層的應用物件(activities, broadcast receivers, etc)和他們建立的視窗。你可以建立你自己的執行緒,然後通過Handler與主執行緒溝通。就像上述說的通過post和sendMessage的方式,Runnable和Message會被計劃的執行在Handler的訊息佇列中適時的進行處理。
二. Message類介紹
1). 檢視Android官網API Message類
java.lang.Object
android.os.Message定義一個message包含描述資訊和任意的資料物件傳送給Handler。這個物件包含兩個額外的int型別的屬性和一個Object型別的屬性,它可以讓你不需要去做一些強制型別的轉換的操作。如下圖所示:
1) arg1 和 arg2 都是Message自帶的用來傳遞一些輕量級儲存int型別的資料,比如進度條的資料等。通過這個資料是通過Bundle的方式來轉載的,讀者可以自己查閱原始碼研究。
2) obj 是Message自帶的Object型別物件,用來傳遞一些物件。相容性最高避免對齊進行型別轉換等。
3) replyTo 是作為執行緒通訊的時候使用.
4) what 使用者自定義的訊息碼讓接受者識別訊息種類,int型別。
【注意】: 獲得Message的構造方法最好的方式是呼叫Message.obtain() 和 Handler.obtainMessage()方法。以便能夠更好被回收池所回收[這裡讀者可以研究一下obtain()的原始碼即可明白]。而不是直接用 new Message的方式來獲得Message物件。
三. 程式Demo
1. 實現通過 Thread + Handler + Message 的方式下載網路資料。程式結構如下圖所示
2. 在Manifest.xml中新增網路許可權,這裡不再貼出,讀者可以參考上面一講內容
3. 佈局檔案中 activity_main.xml 中定義Button和ImageView控制元件,這裡不再貼出,讀者可以自己下載原始碼檢視
4. MainActivity.java 程式主程式碼
... /** * 通過 Handler + Message 的方式下載網路資料 * 通過子執行緒run()方法中下載資料,使用Message攜帶資料,然後用Handler傳送訊息並且處理訊息來更新UI. * * @author AHuier */ public class MainActivity extends Activity { private Button btn; private ImageView imageView; private String imgPath = "http://f.hiphotos.baidu.com/image/w%3D2048/sign=05793c21bba1cd1105b675208d2ac9fc/43a7d933c895d14350ee3c3272f082025aaf0703.jpg"; private static final int DOWNLOAD_IMG = 1; private ProgressDialog dialog = null; private Handler handler = new Handler() { // 處理子執行緒給我們傳送的訊息。 @Override public void handleMessage(android.os.Message msg) { byte[] data = (byte[])msg.obj; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); imageView.setImageBitmap(bitmap); if(msg.what == DOWNLOAD_IMG){ dialog.dismiss(); } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initComponent(); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub new Thread(new MyThread()).start(); dialog.show(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } private void initComponent() { btn = (Button) this.findViewById(R.id.button1); imageView = (ImageView) this.findViewById(R.id.imageView1); dialog = new ProgressDialog(this); dialog.setTitle("提示"); dialog.setMessage("正在下載,請稍後..."); dialog.setCancelable(false); } // 使用Handler Message MessageQueue Looper等方式去訪問網路資源的時候,我們必須要開啟一個子執行緒 public class MyThread implements Runnable{ // 在run方法中完成網路耗時的操作 @Override public void run() { HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(imgPath); HttpResponse httpResponse = null; try { httpResponse = httpClient.execute(httpGet); if(200 == httpResponse.getStatusLine().getStatusCode()){ byte[] data = EntityUtils.toByteArray(httpResponse.getEntity()); // 這裡的資料data我們必須傳送給UI的主執行緒,所以我們通過Message的方式來做橋樑。 Message message = Message.obtain(); message.obj = data; message.what = DOWNLOAD_IMG; handler.sendMessage(message); } } catch (Exception e) { // TODO: handle exception } } } }
5. 程式執行結果
四、程式Demo總結
1. 上述Demo中的Handler寫法我們只是按圖索驥的方式實現比較標準的寫法,如果考慮android記憶體機制的情況下,private Handler的方式定義成為靜態的會更好。在Android原始碼中Handler一般是定義成 protect 許可權的。
2.Handler主要是用來負責傳送訊息和處理訊息的。
3.基於處理,其實這裡面蘊含著一個訊息佇列的概念,這裡為什麼我們獲得訊息需要用到Obtain()的方式,而不是通過new的方式構建一個訊息,這個問題下一講我們會通過剖析Message中Obtain()的原始碼來討論。
更多關於Android訊息處理機制請點選以下相關連結: