1. 程式人生 > >安卓學習筆記(一)——執行緒的用法及怎樣在子執行緒中更新UI

安卓學習筆記(一)——執行緒的用法及怎樣在子執行緒中更新UI

建立執行緒方法:

1、新建一個繼承自Thread的類MyThread,然後重寫父類的run()方法,在裡面寫要執行的內容;

class MyThread extends Thread {

@Override
public void run() {
// 處理具體的邏輯
}

}

啟動執行緒時,需要new出MyThread的例項,然後呼叫它的start()方法。

new MyThread().start();

2、定義一個雷MyThread,並實現Runnable介面,然後重寫run()方法;

class MyThread implements Runnable {
  
  @Override
  public void run() {
    // 處理具體的邏輯
  }

}

啟動執行緒時,先要new出MyThread的例項,然後將物件傳給new出的Thread例項,再呼叫Thread的start方法。

MyThread myThread = new MyThread();
new Thread(myThread).start();

3、使用匿名類的方式。

new Thread(new Runnable() {
  
  @Override
  public void run() {
    // 處理具體的邏輯
  }
  
}).start();

在子執行緒中更新UI:由於安卓更新UI元素必須在主執行緒中,否則就會出現異常。有時候我們必須在子執行緒裡去執行一些耗時任務,然後根據任務的執行結果來更新相應的UI控制元件,比如:執行網路請求,需要將傳回的資料更新到UI上。

操作方法:

一、在主執行緒中new出Handler的例項handler,在Handler的handleMessage()方法中傳回的Message物件屬性進行判斷,當滿足要求時,執行UI更新的操作。

在子執行緒的run()方法內,1、例項化Message物件message;2、物件呼叫Message的what變數,設定message的屬性;3、物件呼叫sendMessage()方法,將Message物件傳送出去。

private Handler handler = new Handler() {
  public void handleMessage(Message msg) {

    switch (msg.what

) {
    case UPDATE_TEXT:
      // 在這裡可以進行UI操作
      text.setText("Nice to meet you");
      break;
    default:
      break;
    }
  }

new Thread(new Runnable() {
  @Override
  public void run() {
    Message message = new Message();
    message.what = UPDATE_TEXT;
    handler.sendMessage(message); // 將Message物件傳送出去
  }
}).start();

安卓中的非同步訊息處理主要有四個部分組成:Message、Handler、MessageQueue、Looper

1、Message:Message是線上程之間傳遞的訊息,它可以攜帶少量的資訊,用於在不同執行緒之間交換資料。可以攜帶what欄位、使用arg1和arg2欄位來攜帶一些整形資料、使用obj欄位攜帶一個Object物件。

2、Handler:主要用於傳送和處理訊息。傳送訊息使用Handler的sendMessage()方法;發出的訊息經過處理後,最終會傳遞到Handler的handleMessage()方法中。

3、MessageQueue:主要用於存放所有通過Handler傳送的訊息。這部分訊息會一直存在於訊息對列中,等待被處理。每個執行緒中只會有一個MessageQueue物件。

4、Looper:Looper是每個執行緒中的MessageQueue的管家,呼叫Looper的loop()方法後,就會進入到一個無限迴圈當中,然後每當發現MessageQueue中存在一條訊息,就會將它取出,並傳遞到Handler的handleMessage()方法中。每個執行緒中也只有一個Looper物件。

二、使用AsyncTask:由於AsyncTask是一個抽象類,所以如果我們想使用它,就必須建立一個子類去繼承它。在繼承時我們可以為AsyncTask類指定三個泛型引數,如下:

1、Params:在執行AsyncTask時需要傳入的引數,可用於在後臺任務中使用。

2、Progress:後臺任務執行時,如果需要在介面上顯示當前的進度,則使用這裡指定的泛型作為進度單位。

3、Result:當任務執行完畢後,如果需要對結果進行返回,則使用這裡指定的泛型作為返回型別。

我們還需要去重寫AsyncTask中的幾個方法才能完成對任務的定製。經常需要去重寫的方法有以下四個。

1.  onPreExecute()
這個方法會在後臺任務開始執行之前呼叫,用於進行一些介面上的初始化操作,比
如顯示一個進度條對話方塊等。
2.  doInBackground(Params...)
這個方法中的所有程式碼都會在子執行緒中執行,我們應該在這裡去處理所有的耗時務。任務一旦完成就可以通過 return語句來將任務的執行結果返回,如果AsyncTask的第三個泛型引數指定的是Void,就可以不返回任務執行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以呼叫publishProgress(Progress...)方法來完成。
3.  onProgressUpdate(Progress...)
當在後臺任務中呼叫了 publishProgress(Progress...)方法後,這個方法就會很快被調
用,方法中攜帶的引數就是在後臺任務中傳遞過來的。在這個方法中可以對 UI進行操作,利用引數中的數值就可以對介面元素進行相應地更新。
4.  onPostExecute(Result)
當後臺任務執行完畢並通過 return語句進行返回時,這個方法就很快會被呼叫。返
回的資料會作為引數傳遞到此方法中,可以利用返回的資料來進行一些 UI操作,比如說提醒任務執行的結果,以及關閉掉進度條對話方塊等。

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
 
  @Override 

 protected void onPreExecute() {
    progressDialog.show(); // 顯示進度對話方塊
  }
 
  @Override
  protected Boolean doInBackground(Void... params) {
    try {
      while (true) {
        int downloadPercent = doDownload(); // 這是一個虛構的方法
        publishProgress(downloadPercent);
        if (downloadPercent >= 100) {
          break;
        }
      }

    } catch (Exception e) {
      return false;
    }
    return true;
  }
 
  @Override
  protected void onProgressUpdate(Integer... values) {
    // 在這裡更新下載進度
    progressDialog.setMessage("Downloaded " + values[0] + "%");
  }
 
  @Override
  protected void onPostExecute(Boolean result) {
    progressDialog.dismiss(); // 關閉進度對話方塊
    // 在這裡提示下載結果
    if (result) {
      Toast.makeText(context, "Download succeeded",Toast.LENGTH_SHORT).show();
    } else {
      Toast.makeText(context, " Download failed",
Toast.LENGTH_SHORT).show();
    }
  }
}

設定完後就可以啟動這個任務:new DownloadTask().execute();