前言
-
多線程的應用在Android開發中是非常常見的,常用方法主要有:
- 繼承Thread類
- 實現Runnable接口
- Handler
- AsyncTask
- HandlerThread
今天,我將獻上一份
AsyncTask
使用教程,希望大家會喜歡
Carson帶你學多線程系列
基礎匯總
Android多線程:基礎知識匯總
基礎使用
Android多線程:繼承Thread類使用(含實例教程)
Android多線程:實現Runnable接口使用(含實例教程)
復合使用
Android 多線程:AsyncTask使用教程(含實例講解)
Android 多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學
相關使用
Android異步通信:這是一份全面&詳細的Handler機制學習攻略
Android多線程:手把手教你全面學習神秘的Synchronized關鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
目錄
1. 定義
一個Android
已封裝好的輕量級異步類。屬于抽象類,即使用時需實現子類。
public abstract class AsyncTask<Params, Progress, Result> {
...
}
2. 作用
- 實現多線程:在工作線程中執行任務,如 耗時任務
- 異步通信、消息傳遞:實現工作線程 & 主線程(
UI
線程)之間的通信,即:將工作線程的執行結果傳遞給主線程,從而在主線程中執行相關的UI
操作,保證線程安全。
3. 優點
- 方便實現異步通信
不需使用 “任務線程(如繼承Thread
類) +Handler
”的復雜組合 - 節省資源
采用線程池的緩存線程 + 復用線程,避免了頻繁創建 & 銷毀線程所帶來的系統資源開銷
4. 類 & 方法介紹
4.1 類定義
AsyncTask
類屬于抽象類,即使用時需 實現子類
public abstract class AsyncTask<Params, Progress, Result> {
...
}
// 類中參數為3種泛型類型
// 整體作用:控制AsyncTask子類執行線程任務時各個階段的返回類型
// 具體說明:
// a. Params:開始異步任務執行時傳入的參數類型,對應excute()中傳遞的參數
// b. Progress:異步任務執行過程中,返回下載進度值的類型
// c. Result:異步任務執行完成后,返回的結果類型,與doInBackground()的返回值類型保持一致
// 注:
// a. 使用時并不是所有類型都被使用
// b. 若無被使用,可用java.lang.Void類型代替
// c. 若有不同業務,需額外再寫1個AsyncTask的子類
}
4.2 核心方法
常用方法執行順序如下
5. 使用步驟
-
AsyncTask
的使用步驟有3個:
- 創建
AsyncTask
子類 & 根據需求實現核心方法 - 創建
AsyncTask
子類的實例對象(即 任務實例) - 手動調用
execute()
從而執行異步線程任務
- 具體介紹如下
/**
* 步驟1:創建AsyncTask子類
* 注:
* a. 繼承AsyncTask類
* b. 為3個泛型參數指定類型;若不使用,可用java.lang.Void類型代替
* c. 根據需求,在AsyncTask子類內實現核心方法
*/
private class MyTask extends AsyncTask<Params, Progress, Result> {
....
// 方法1:onPreExecute()
// 作用:執行 線程任務前的操作
// 注:根據需求復寫
@Override
protected void onPreExecute() {
...
}
// 方法2:doInBackground()
// 作用:接收輸入參數、執行任務中的耗時操作、返回 線程任務執行的結果
// 注:必須復寫,從而自定義線程任務
@Override
protected String doInBackground(String... params) {
...// 自定義的線程任務
// 可調用publishProgress()顯示進度, 之后將執行onProgressUpdate()
publishProgress(count);
}
// 方法3:onProgressUpdate()
// 作用:在主線程 顯示線程任務執行的進度
// 注:根據需求復寫
@Override
protected void onProgressUpdate(Integer... progresses) {
...
}
// 方法4:onPostExecute()
// 作用:接收線程任務執行結果、將執行結果顯示到UI組件
// 注:必須復寫,從而自定義UI操作
@Override
protected void onPostExecute(String result) {
...// UI操作
}
// 方法5:onCancelled()
// 作用:將異步任務設置為:取消狀態
@Override
protected void onCancelled() {
...
}
}
/**
* 步驟2:創建AsyncTask子類的實例對象(即 任務實例)
* 注:AsyncTask子類的實例必須在UI線程中創建
*/
MyTask mTask = new MyTask();
/**
* 步驟3:手動調用execute(Params... params) 從而執行異步線程任務
* 注:
* a. 必須在UI線程中調用
* b. 同一個AsyncTask實例對象只能執行1次,若執行第2次將會拋出異常
* c. 執行任務中,系統會自動調用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
* d. 不能手動調用上述方法
*/
mTask.execute();
6. 實例講解
下面,我將用1個實例講解 具體如何使用 AsyncTask
6.1 實例說明
- 點擊按鈕 則 開啟線程執行線程任務
- 顯示后臺加載進度
- 加載完畢后更新UI組件
- 期間若點擊取消按鈕,則取消加載
如下圖
6.2 具體實現
建議先下載源碼再看:Carson_Ho的Github地址:AsyncTask
- 主布局文件:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context="com.example.carson_ho.handler_learning.MainActivity">
<Button
android:layout_centerInParent="true"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點我加載"/>
<TextView
android:id="@+id/text"
android:layout_below="@+id/button"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="還沒開始加載!" />
<ProgressBar
android:layout_below="@+id/text"
android:id="@+id/progress_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:progress="0"
android:max="100"
style="?android:attr/progressBarStyleHorizontal"/>
<Button
android:layout_below="@+id/progress_bar"
android:layout_centerInParent="true"
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cancel"/>
</RelativeLayout>
- 主邏輯代碼文件:MainActivity.java
public class MainActivity extends AppCompatActivity {
// 線程變量
MyTask mTask;
// 主布局中的UI組件
Button button,cancel; // 加載、取消按鈕
TextView text; // 更新的UI組件
ProgressBar progressBar; // 進度條
/**
* 步驟1:創建AsyncTask子類
* 注:
* a. 繼承AsyncTask類
* b. 為3個泛型參數指定類型;若不使用,可用java.lang.Void類型代替
* 此處指定為:輸入參數 = String類型、執行進度 = Integer類型、執行結果 = String類型
* c. 根據需求,在AsyncTask子類內實現核心方法
*/
private class MyTask extends AsyncTask<String, Integer, String> {
// 方法1:onPreExecute()
// 作用:執行 線程任務前的操作
@Override
protected void onPreExecute() {
text.setText("加載中");
// 執行前顯示提示
}
// 方法2:doInBackground()
// 作用:接收輸入參數、執行任務中的耗時操作、返回 線程任務執行的結果
// 此處通過計算從而模擬“加載進度”的情況
@Override
protected String doInBackground(String... params) {
try {
int count = 0;
int length = 1;
while (count<99) {
count += length;
// 可調用publishProgress()顯示進度, 之后將執行onProgressUpdate()
publishProgress(count);
// 模擬耗時任務
Thread.sleep(50);
}
}catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
// 方法3:onProgressUpdate()
// 作用:在主線程 顯示線程任務執行的進度
@Override
protected void onProgressUpdate(Integer... progresses) {
progressBar.setProgress(progresses[0]);
text.setText("loading..." + progresses[0] + "%");
}
// 方法4:onPostExecute()
// 作用:接收線程任務執行結果、將執行結果顯示到UI組件
@Override
protected void onPostExecute(String result) {
// 執行完畢后,則更新UI
text.setText("加載完畢");
}
// 方法5:onCancelled()
// 作用:將異步任務設置為:取消狀態
@Override
protected void onCancelled() {
text.setText("已取消");
progressBar.setProgress(0);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 綁定UI組件
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
cancel = (Button) findViewById(R.id.cancel);
text = (TextView) findViewById(R.id.text);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
/**
* 步驟2:創建AsyncTask子類的實例對象(即 任務實例)
* 注:AsyncTask子類的實例必須在UI線程中創建
*/
mTask = new MyTask();
// 加載按鈕按按下時,則啟動AsyncTask
// 任務完成后更新TextView的文本
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* 步驟3:手動調用execute(Params... params) 從而執行異步線程任務
* 注:
* a. 必須在UI線程中調用
* b. 同一個AsyncTask實例對象只能執行1次,若執行第2次將會拋出異常
* c. 執行任務中,系統會自動調用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
* d. 不能手動調用上述方法
*/
mTask.execute();
}
});
cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 取消一個正在執行的任務,onCancelled方法將會被調用
mTask.cancel(true);
}
});
}
}
- 運行結果
7. 使用時的注意點
在使用AsyncTask
時有一些問題需要注意的:
7.1 關于 生命周期
- 結論
AsyncTask
不與任何組件綁定生命周期 - 使用建議
在Activity
或Fragment
中使用AsyncTask
時,最好在Activity
或Fragment
的onDestory()
調用cancel(boolean)
;
7.2 關于 內存泄漏
- 結論
若AsyncTask
被聲明為Activity
的非靜態內部類,當Activity
需銷毀時,會因AsyncTask
保留對Activity
的引用 而導致Activity
無法被回收,最終引起內存泄露 - 使用建議
AsyncTask
應被聲明為Activity
的靜態內部類
7.3 線程任務執行結果 丟失
- 結論
當Activity
重新創建時(屏幕旋轉 /Activity
被意外銷毀時后恢復),之前運行的AsyncTask
(非靜態的內部類)持有的之前Activity
引用已無效,故復寫的onPostExecute()
將不生效,即無法更新UI操作 - 使用建議
在Activity
恢復時的對應方法 重啟 任務線程
8. 源碼分析
- 知其然 而須知其所以然,了解
AsyncTask
的源碼分析有利于更好地理解AsyncTask
的工作原理 - 具體請看文章:Android 多線程:AsyncTask的原理 及其源碼分析
9. 總結
- 本文全面介紹了多線程中的
AsyncTask
,含使用方法、工作原理 & 源碼分析 - 下一篇文章我將對講解
Android
多線程的相關知識,感興趣的同學可以繼續關注Carson_Ho的簡書
Carson帶你學多線程系列
基礎匯總
Android多線程:基礎知識匯總
基礎使用
Android多線程:繼承Thread類使用(含實例教程)
Android多線程:實現Runnable接口使用(含實例教程)
復合使用
Android 多線程:AsyncTask使用教程(含實例講解)
Android 多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學
相關使用
Android異步通信:這是一份全面&詳細的Handler機制學習攻略
Android多線程:手把手教你全面學習神秘的Synchronized關鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
歡迎關注Carson_Ho的簡書
不定期分享關于安卓開發的干貨,追求短、平、快,但卻不缺深度。