安卓學習筆記(一)——執行緒的用法及怎樣在子執行緒中更新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();