前言
-
多線程的應(yīng)用在Android開發(fā)中是非常常見的,常用方法主要有:
- 繼承Thread類
- 實現(xiàn)Runnable接口
- Handler
- AsyncTask
- HandlerThread
今天坎弯,我將獻(xiàn)上一份
AsyncTask
使用教程,希望大家會喜歡
Carson帶你學(xué)多線程系列
基礎(chǔ)匯總
Android多線程:基礎(chǔ)知識匯總
基礎(chǔ)使用
Android多線程:繼承Thread類使用(含實例教程)
Android多線程:實現(xiàn)Runnable接口使用(含實例教程)
復(fù)合使用
Android 多線程:AsyncTask使用教程(含實例講解)
Android 多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學(xué)
相關(guān)使用
Android異步通信:這是一份全面&詳細(xì)的Handler機(jī)制學(xué)習(xí)攻略
Android多線程:手把手教你全面學(xué)習(xí)神秘的Synchronized關(guān)鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
目錄
1. 定義
一個Android
已封裝好的輕量級異步類伞剑。屬于抽象類揭措,即使用時需實現(xiàn)子類。
public abstract class AsyncTask<Params, Progress, Result> {
...
}
2. 作用
- 實現(xiàn)多線程:在工作線程中執(zhí)行任務(wù)完慧,如 耗時任務(wù)
- 異步通信谋旦、消息傳遞:實現(xiàn)工作線程 & 主線程(
UI
線程)之間的通信,即:將工作線程的執(zhí)行結(jié)果傳遞給主線程屈尼,從而在主線程中執(zhí)行相關(guān)的UI
操作册着,保證線程安全。
3. 優(yōu)點
- 方便實現(xiàn)異步通信
不需使用 “任務(wù)線程(如繼承Thread
類) +Handler
”的復(fù)雜組合 - 節(jié)省資源
采用線程池的緩存線程 + 復(fù)用線程脾歧,避免了頻繁創(chuàng)建 & 銷毀線程所帶來的系統(tǒng)資源開銷
4. 類 & 方法介紹
4.1 類定義
AsyncTask
類屬于抽象類甲捏,即使用時需 實現(xiàn)子類
public abstract class AsyncTask<Params, Progress, Result> {
...
}
// 類中參數(shù)為3種泛型類型
// 整體作用:控制AsyncTask子類執(zhí)行線程任務(wù)時各個階段的返回類型
// 具體說明:
// a. Params:開始異步任務(wù)執(zhí)行時傳入的參數(shù)類型,對應(yīng)excute()中傳遞的參數(shù)
// b. Progress:異步任務(wù)執(zhí)行過程中鞭执,返回下載進(jìn)度值的類型
// c. Result:異步任務(wù)執(zhí)行完成后司顿,返回的結(jié)果類型,與doInBackground()的返回值類型保持一致
// 注:
// a. 使用時并不是所有類型都被使用
// b. 若無被使用兄纺,可用java.lang.Void類型代替
// c. 若有不同業(yè)務(wù)大溜,需額外再寫1個AsyncTask的子類
}
4.2 核心方法
常用方法執(zhí)行順序如下
5. 使用步驟
-
AsyncTask
的使用步驟有3個:
- 創(chuàng)建
AsyncTask
子類 & 根據(jù)需求實現(xiàn)核心方法 - 創(chuàng)建
AsyncTask
子類的實例對象(即 任務(wù)實例) - 手動調(diào)用
execute()
從而執(zhí)行異步線程任務(wù)
- 具體介紹如下
/**
* 步驟1:創(chuàng)建AsyncTask子類
* 注:
* a. 繼承AsyncTask類
* b. 為3個泛型參數(shù)指定類型;若不使用估脆,可用java.lang.Void類型代替
* c. 根據(jù)需求钦奋,在AsyncTask子類內(nèi)實現(xiàn)核心方法
*/
private class MyTask extends AsyncTask<Params, Progress, Result> {
....
// 方法1:onPreExecute()
// 作用:執(zhí)行 線程任務(wù)前的操作
// 注:根據(jù)需求復(fù)寫
@Override
protected void onPreExecute() {
...
}
// 方法2:doInBackground()
// 作用:接收輸入?yún)?shù)、執(zhí)行任務(wù)中的耗時操作旁蔼、返回 線程任務(wù)執(zhí)行的結(jié)果
// 注:必須復(fù)寫锨苏,從而自定義線程任務(wù)
@Override
protected String doInBackground(String... params) {
...// 自定義的線程任務(wù)
// 可調(diào)用publishProgress()顯示進(jìn)度, 之后將執(zhí)行onProgressUpdate()
publishProgress(count);
}
// 方法3:onProgressUpdate()
// 作用:在主線程 顯示線程任務(wù)執(zhí)行的進(jìn)度
// 注:根據(jù)需求復(fù)寫
@Override
protected void onProgressUpdate(Integer... progresses) {
...
}
// 方法4:onPostExecute()
// 作用:接收線程任務(wù)執(zhí)行結(jié)果、將執(zhí)行結(jié)果顯示到UI組件
// 注:必須復(fù)寫棺聊,從而自定義UI操作
@Override
protected void onPostExecute(String result) {
...// UI操作
}
// 方法5:onCancelled()
// 作用:將異步任務(wù)設(shè)置為:取消狀態(tài)
@Override
protected void onCancelled() {
...
}
}
/**
* 步驟2:創(chuàng)建AsyncTask子類的實例對象(即 任務(wù)實例)
* 注:AsyncTask子類的實例必須在UI線程中創(chuàng)建
*/
MyTask mTask = new MyTask();
/**
* 步驟3:手動調(diào)用execute(Params... params) 從而執(zhí)行異步線程任務(wù)
* 注:
* a. 必須在UI線程中調(diào)用
* b. 同一個AsyncTask實例對象只能執(zhí)行1次伞租,若執(zhí)行第2次將會拋出異常
* c. 執(zhí)行任務(wù)中,系統(tǒng)會自動調(diào)用AsyncTask的一系列方法:onPreExecute() 限佩、doInBackground()葵诈、onProgressUpdate() 裸弦、onPostExecute()
* d. 不能手動調(diào)用上述方法
*/
mTask.execute();
6. 實例講解
下面作喘,我將用1個實例講解 具體如何使用 AsyncTask
6.1 實例說明
- 點擊按鈕 則 開啟線程執(zhí)行線程任務(wù)
- 顯示后臺加載進(jìn)度
- 加載完畢后更新UI組件
- 期間若點擊取消按鈕理疙,則取消加載
如下圖
6.2 具體實現(xiàn)
建議先下載源碼再看: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; // 進(jìn)度條
/**
* 步驟1:創(chuàng)建AsyncTask子類
* 注:
* a. 繼承AsyncTask類
* b. 為3個泛型參數(shù)指定類型泞坦;若不使用窖贤,可用java.lang.Void類型代替
* 此處指定為:輸入?yún)?shù) = String類型、執(zhí)行進(jìn)度 = Integer類型贰锁、執(zhí)行結(jié)果 = String類型
* c. 根據(jù)需求赃梧,在AsyncTask子類內(nèi)實現(xiàn)核心方法
*/
private class MyTask extends AsyncTask<String, Integer, String> {
// 方法1:onPreExecute()
// 作用:執(zhí)行 線程任務(wù)前的操作
@Override
protected void onPreExecute() {
text.setText("加載中");
// 執(zhí)行前顯示提示
}
// 方法2:doInBackground()
// 作用:接收輸入?yún)?shù)、執(zhí)行任務(wù)中的耗時操作豌熄、返回 線程任務(wù)執(zhí)行的結(jié)果
// 此處通過計算從而模擬“加載進(jìn)度”的情況
@Override
protected String doInBackground(String... params) {
try {
int count = 0;
int length = 1;
while (count<99) {
count += length;
// 可調(diào)用publishProgress()顯示進(jìn)度, 之后將執(zhí)行onProgressUpdate()
publishProgress(count);
// 模擬耗時任務(wù)
Thread.sleep(50);
}
}catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
// 方法3:onProgressUpdate()
// 作用:在主線程 顯示線程任務(wù)執(zhí)行的進(jìn)度
@Override
protected void onProgressUpdate(Integer... progresses) {
progressBar.setProgress(progresses[0]);
text.setText("loading..." + progresses[0] + "%");
}
// 方法4:onPostExecute()
// 作用:接收線程任務(wù)執(zhí)行結(jié)果授嘀、將執(zhí)行結(jié)果顯示到UI組件
@Override
protected void onPostExecute(String result) {
// 執(zhí)行完畢后,則更新UI
text.setText("加載完畢");
}
// 方法5:onCancelled()
// 作用:將異步任務(wù)設(shè)置為:取消狀態(tài)
@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:創(chuàng)建AsyncTask子類的實例對象(即 任務(wù)實例)
* 注:AsyncTask子類的實例必須在UI線程中創(chuàng)建
*/
mTask = new MyTask();
// 加載按鈕按按下時锣险,則啟動AsyncTask
// 任務(wù)完成后更新TextView的文本
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* 步驟3:手動調(diào)用execute(Params... params) 從而執(zhí)行異步線程任務(wù)
* 注:
* a. 必須在UI線程中調(diào)用
* b. 同一個AsyncTask實例對象只能執(zhí)行1次蹄皱,若執(zhí)行第2次將會拋出異常
* c. 執(zhí)行任務(wù)中,系統(tǒng)會自動調(diào)用AsyncTask的一系列方法:onPreExecute() 芯肤、doInBackground()巷折、onProgressUpdate() 、onPostExecute()
* d. 不能手動調(diào)用上述方法
*/
mTask.execute();
}
});
cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 取消一個正在執(zhí)行的任務(wù),onCancelled方法將會被調(diào)用
mTask.cancel(true);
}
});
}
}
- 運行結(jié)果
7. 使用時的注意點
在使用AsyncTask
時有一些問題需要注意的:
7.1 關(guān)于 生命周期
- 結(jié)論
AsyncTask
不與任何組件綁定生命周期 - 使用建議
在Activity
或Fragment
中使用AsyncTask
時纷妆,最好在Activity
或Fragment
的onDestory()
調(diào)用cancel(boolean)
盔几;
7.2 關(guān)于 內(nèi)存泄漏
- 結(jié)論
若AsyncTask
被聲明為Activity
的非靜態(tài)內(nèi)部類,當(dāng)Activity
需銷毀時掩幢,會因AsyncTask
保留對Activity
的引用 而導(dǎo)致Activity
無法被回收逊拍,最終引起內(nèi)存泄露 - 使用建議
AsyncTask
應(yīng)被聲明為Activity
的靜態(tài)內(nèi)部類
7.3 線程任務(wù)執(zhí)行結(jié)果 丟失
- 結(jié)論
當(dāng)Activity
重新創(chuàng)建時(屏幕旋轉(zhuǎn) /Activity
被意外銷毀時后恢復(fù)),之前運行的AsyncTask
(非靜態(tài)的內(nèi)部類)持有的之前Activity
引用已無效际邻,故復(fù)寫的onPostExecute()
將不生效芯丧,即無法更新UI操作 - 使用建議
在Activity
恢復(fù)時的對應(yīng)方法 重啟 任務(wù)線程
8. 源碼分析
- 知其然 而須知其所以然,了解
AsyncTask
的源碼分析有利于更好地理解AsyncTask
的工作原理 - 具體請看文章:Android 多線程:AsyncTask的原理 及其源碼分析
9. 總結(jié)
- 本文全面介紹了多線程中的
AsyncTask
世曾,含使用方法缨恒、工作原理 & 源碼分析 - 下一篇文章我將對講解
Android
多線程的相關(guān)知識,感興趣的同學(xué)可以繼續(xù)關(guān)注Carson_Ho的簡書
Carson帶你學(xué)多線程系列
基礎(chǔ)匯總
Android多線程:基礎(chǔ)知識匯總
基礎(chǔ)使用
Android多線程:繼承Thread類使用(含實例教程)
Android多線程:實現(xiàn)Runnable接口使用(含實例教程)
復(fù)合使用
Android 多線程:AsyncTask使用教程(含實例講解)
Android 多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學(xué)
相關(guān)使用
Android異步通信:這是一份全面&詳細(xì)的Handler機(jī)制學(xué)習(xí)攻略
Android多線程:手把手教你全面學(xué)習(xí)神秘的Synchronized關(guān)鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
歡迎關(guān)注Carson_Ho的簡書
不定期分享關(guān)于安卓開發(fā)的干貨轮听,追求短骗露、平、快血巍,但卻不缺深度萧锉。