Message:消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。
Handler:處理者,負責Message的發送及處理。使用Handler時,需要實現handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。
MessageQueue:消息隊列,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message以鏈表的方式串聯起來的,等待Looper的抽取。
Looper:消息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。
Thread:線程,負責調度整個消息循環,即消息循環的執行場所。
方法一:用Handler
1、主線程中定義Handler:
- Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case 0:
- //完成主界面更新,拿到數據
- String data = (String)msg.obj;
- updateWeather();
- textView.setText(data);
- break;
- default:
- break;
- }
- }
- };
2、子線程發消息,通知Handler完成UI更新:
- private void updateWeather() {
- new Thread(new Runnable(){
- @Override
- public void run() {
- //耗時操作,完成之後發送消息給Handler,完成UI更新;
- mHandler.sendEmptyMessage(0);
- //需要數據傳遞,用下面方法;
- Message msg =new Message();
- msg.obj = "數據";//可以是基本類型,可以是對象,可以是List、map等;
- mHandler.sendMessage(msg);
- }
- }).start();
- }
方法一的Handler對象必須定義在主線程中,如果是多個類直接互相調用,就不是很方便,需要傳遞content對象或通過接口調用;
方法二:用Activity對象的runOnUiThread方法更新
在子線程中通過runOnUiThread()方法更新UI:
- new Thread() {
- public void run() {
- //這兒是耗時操作,完成之後更新UI;
- runOnUiThread(new Runnable(){
- @Override
- public void run() {
- //更新UI
- imageView.setImageBitmap(bitmap);
- }
- });
- }
- }.start();
如果在非上下文類中(Activity),可以通過傳遞上下文實現調用;
- Activity activity = (Activity) imageView.getContext();
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- imageView.setImageBitmap(bitmap);
- }
- });
這種方法使用比較靈活,但如果Thread定義在其他地方,需要傳遞Activity對象;
方法三:View.post(Runnable r)
子線程如果持有某個View的引用,要對該View進行更新,則可調用該View對象的post(Runnable r)或postDelay(Runnable r)方法
Handler對象也有post()方法。其實在Android的源碼中,這些post()方法都是藉助下面的第3種方法:Handler + Message來實現的。
- imageView.post(new Runnable(){
- @Override
- public void run() {
- imageView.setImageBitmap(bitmap);
- }
- });
4、Broadcast
子線程中發送廣播,主線程中接收廣播並更新UI
5、AsyncTask
AsyncTask可方便地實現新開一個線程,並將結果返回給UI線程,而不需要開發者手動去新開一個線程,也無須開發者使用Handler,非常方便。
應當注意的是AsyncTask是一個抽象類,其三個泛型參數的意義如下:
AsyncTask<Param, Progress, Result>
Param:發送給新開的線程的參數類型
Progress:表徵任務處理進度的類型。
Result:線程任務處理完之後,返回給UI線程的值的類型。
該類中有四個抽象函數,onPreExecute(), doInBackground(Params... param),
onProgressUpdate(Progress... progress), onPostExecute(Result result)。
除了,doInBackground(Params...)方法,其它三個方法都運行在UI線程。
自定義一個類繼承AsyncTask並至少實現doInBackground()函數。在該函數中執行的過程中,可以隨時調用publishProgress(Progress...)報告其執行進度。此時會觸發另一個方法onProgressUpdate(Progress... progress),以便在UI線程中的某些控件(如ProgressBar)上更新任務處理的進度。
也可以等doInBackground()執行完,進入onPostExecute()方法後,再進行UI控件的更新。
可在任意時間,任意線程中,取消AsyncTask開啓的任務(調用自定義的AsynTask子類的cancel(boolean mayInterruptIfRunning)方法)
使用示例如下:
//如果沒記錯的話,這個例子應該是之前總結的時候從官網剪下來的
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
這種方法更簡單,但需要傳遞要更新的View過去;
總結:UI的更新必須在主線程中完成,所以不管上述那種方法,都是將更新UI的消息發送到了主線程的消息對象,讓主線程做處理;