AsyncTask是Android為了簡化異步操作而封裝的異步任務(wù)操作抽象類。當(dāng)我們需要在程序中執(zhí)行耗時的異步操作時腰湾,我們可以考慮使用AsyncTask來實現(xiàn)大诸。
AsyncTask的使用比較簡單钧敞,由于AsyncTask是一個抽象類糊渊,我們需要寫一個類來繼承AsyncTask,繼承AsyncTask需要指定三個泛型參數(shù),參數(shù)分別表示為:
(1)Params:在執(zhí)行AsyncTask時需要傳入的參數(shù)销凑,比如我們輸入的String類型URL
(2)Progress:后臺任務(wù)執(zhí)行中返回的進度值類型丛晌,如果需要在界面上顯示當(dāng)前進度,則就用Progress指定的泛型作為進度單位斗幼。
(3)Result:耗時操作完成后返回的結(jié)果澎蛛,如果需要對結(jié)果進行返回,那么就用這里指定的泛型作為返回的類型孟岛。
實現(xiàn)AsyncTask類還需要重寫相應(yīng)的回調(diào)方法瓶竭,一般我們用到的有如下四個方法,他們都是自動被調(diào)用的渠羞,記得不要手動的調(diào)用:
(1)doInBackground(Void... params):必須重寫斤贰,異步執(zhí)行耗時操作
(2)onPreExecute():執(zhí)行耗時操作前被調(diào)用,通常用來完成一些初始化操作
(3)onProgressUpdate(Void... values):將doInBackground方法返回的值傳遞給該方法次询,在doInBackground中調(diào)用publishProgroess()方法可以且來更新進度
(4)onPostExecute(Void result):耗時的異步任務(wù)完成后回調(diào)該方法
需要說明一點:這幾個方法中荧恍,只有doInBackground方法是在子線程中執(zhí)行的,其他的方法都是在主線程中執(zhí)行的。
啟動和取消這個異步任務(wù)的方法分別是:
(1)myAsyncTask.execute() 其中該任務(wù)
(2)myAsyncTask.cancel() 取消該任務(wù)
我們已經(jīng)大概了解了AsyncTask的基礎(chǔ)知識送巡,接下來就以如下三個點做一些實例摹菠,加深一下印象。
(一)觀察AsyncTask子類方法的執(zhí)行順序
首先寫一個最簡單的AsyncTask子類骗爆,傳入的參數(shù)都是空值次氨, 然后在每一個方法中都打印出一句話,主要是為了便于觀察他們的執(zhí)行順序摘投。
package com.adan.asynctaskdome;
import android.os.AsyncTask;
import android.util.Log;
/**
* @author: xiaolijuan
* @description:
* @projectName: AsyncTaskDome
* @date: 2016-03-04
* @time: 12:47
*/
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
Log.d("TAG", "doInBackground");
return null;
}
@Override
protected void onPreExecute() {
Log.d("TAG", "onPreExecute");
super.onPreExecute();
}
@Override
protected void onPostExecute(Void aVoid) {
Log.d("TAG", "onPostExecute");
super.onPostExecute(aVoid);
}
@Override
protected void onProgressUpdate(Void... values) {
Log.d("TAG", "onProgressUpdate");
super.onProgressUpdate(values);
}
}
MainActivity的代碼
package com.adan.asynctaskdome;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements View.OnClickListener {
private Button button1, button2, button3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyAsyncTask().execute();
}
}
運行結(jié)果:
這里的 方法沒有執(zhí)行煮寡,是因為我們需要在doInBackground(Void... params)中提供publishProgress(Progress...)方法用于顯示進度信息,如下所示:
package com.adan.asynctaskdome;
import android.os.AsyncTask;
import android.util.Log;
/**
* @author: xiaolijuan
* @description:
* @projectName: AsyncTaskDome
* @date: 2016-03-04
* @time: 12:47
*/
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
Log.d("TAG", "doInBackground");
publishProgress();
return null;
}
@Override
protected void onPreExecute() {
Log.d("TAG", "onPreExecute");
super.onPreExecute();
}
@Override
protected void onPostExecute(Void aVoid) {
Log.d("TAG", "onPostExecute");
super.onPostExecute(aVoid);
}
@Override
protected void onProgressUpdate(Void... values) {
Log.d("TAG", "onProgressUpdate");
super.onProgressUpdate(values);
}
}
運行效果:
從打印的結(jié)果很容易就看出方法的執(zhí)行順序了吧犀呼,如下:
onPreExecute——>onProgressUpdate(調(diào)用了publishProgress方法才會調(diào)用該方法)——>doInBackground——>onPostExecute
(二)異步加載網(wǎng)絡(luò)圖片
首先一點先別忘記了幸撕,我們既然是要訪問網(wǎng)絡(luò),千萬不能忘記了在配置文件中添加網(wǎng)絡(luò)權(quán)限哦
<uses-permission android:name="android.permission.INTERNET" />
LoadBitmapActivity外臂,我們使用一個ImageView用來加載我們的這張網(wǎng)絡(luò)圖片的坐儿,然后再在中間放置一個進度條,用來顯示進程
LoadBitmapActivity
package com.adan.asynctaskdome;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* @author: xiaolijuan
* @description: 加載圖片
* @projectName: AsyncTaskDome
* @date: 2016-03-04
* @time: 13:12
*/
public class LoadBitmapActivity extends Activity {
private ImageView img;
private ProgressBar progressBar;
private static final String url = "https://www.baidu.com/img/bd_logo1.png";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_load_bitmap);
img = (ImageView) findViewById(R.id.img);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
new MyAsyncTask().execute(url);
}
class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
/**
* 將url所對應(yīng)的圖像解析成Bitmap
*
* @param params
* @return
*/
protected Bitmap doInBackground(String... params) {
String url = params[0];// 獲取傳遞進來的參數(shù)
Bitmap bitmap = null;
URLConnection connection;//定義網(wǎng)絡(luò)連接對象
InputStream is;//用于獲取數(shù)據(jù)的輸入流
try {
//訪問網(wǎng)絡(luò)操作宋光,耗時操作
connection = new URL(url).openConnection();//獲取網(wǎng)絡(luò)連接對象
is = connection.getInputStream();//獲取輸入流
BufferedInputStream bis = new BufferedInputStream(is);
bitmap = BitmapFactory.decodeStream(bis);//通過decodeStream()方法解析輸入流貌矿,從而轉(zhuǎn)換成一張bitmap圖片
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
//將bitmap作為返回對象
return bitmap;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
//顯示進度條
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
try {
Thread.sleep(2000);//為了觀看效果,休眠2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
super.onPostExecute(bitmap);
//關(guān)閉進度條和更新UI
progressBar.setVisibility(View.GONE);
img.setImageBitmap(bitmap);
}
}
}
activity_load_bitmap.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/img"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerInParent="true" />
</RelativeLayout>
效果圖如下:
(三)顯示進度
ShowProgressActivity跃须,用來顯示這個進度條站叼,在這個類里面我們就使用AsyncTask讓進度條動起來娃兽。
package com.adan.asynctaskdome;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;
/**
* @author: xiaolijuan
* @description: 顯示進度
* @projectName: AsyncTaskDome
* @date: 2016-03-04
* @time: 13:12
*/
public class ShowProgressActivity extends Activity {
private ProgressBar progressBar;
private MyAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_progress);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
mTask = new MyAsyncTask();
mTask.execute();
}
class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100; i++) {
publishProgress(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
//根據(jù)任務(wù)執(zhí)行情況更新UI菇民,其實就是更新進度條
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 進度條數(shù)值顯示
progressBar.setProgress(values[0]);
}
}
}
activity_show_progress,布局文件很簡單投储,里面就放置一個橫向的進度條而已
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="5dp" />
</LinearLayout>
效果圖如下:
不知道大家發(fā)現(xiàn)一個問題沒第练,當(dāng)我第一次點擊界面時候,進度條還沒運行完玛荞,我就返回了娇掏,當(dāng)再一次進去時候,等待了好久進度條才有開始啟動起來勋眯。這是因為我們之前的doInBackground里的線程還在進行婴梧,循環(huán)還在繼續(xù),所以AsyncTask還在執(zhí)行客蹋。因此此刻重新進入進度條界面時塞蹭,就不可能立刻啟動新的AsyncTask,而舊的呢又因為之前我們返回到按鈕界面就沒法重新繪制了讶坯。所以只有等舊的AsyncTask執(zhí)行完畢番电,才會開啟新的。就像隊列一樣,只有運行完之后才運行下一個漱办。這時候你會想这刷,那就麻煩了,如果我需要大量地處理一些耗時操作呢娩井,這樣給用戶的體驗性是不好的暇屋。我們能不能在我們點擊返回的時候就取消了這個任務(wù)呢,答案當(dāng)然是可以的洞辣,只不過我們需要將AsyncTask與Activity的生命周期相關(guān)聯(lián)率碾。
我把修改后的代碼貼一下:
package com.adan.asynctaskdome;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;
/**
* @author: xiaolijuan
* @description: 顯示進度
* @projectName: AsyncTaskDome
* @date: 2016-03-04
* @time: 13:12
*/
public class ShowProgressActivity extends Activity {
private ProgressBar progressBar;
private MyAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_progress);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
mTask = new MyAsyncTask();
mTask.execute();
}
@Override
protected void onPause() {
// 判斷當(dāng)前異步任務(wù)是否在進行中
if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
// cancel()方法:取消該任務(wù),但只是將對應(yīng)的AsyncTask狀態(tài)標(biāo)記為cancel狀態(tài),并沒有真正取消異步操作
mTask.cancel(true);
}
super.onPause();
}
class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100; i++) {
if (isCancelled()) {//如果異步任務(wù)為取消狀態(tài)屋彪,立刻break所宰,跳出循環(huán)
break;
}
publishProgress(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
//根據(jù)任務(wù)執(zhí)行情況更新UI,其實就是更新進度條
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 進度條數(shù)值顯示
progressBar.setProgress(values[0]);
}
}
}
接下來我們再看看效果圖:
最后我們來總結(jié)一下使用AsyncTask類需要注意的幾點:
(1)AsyncTask實例必須在UI Thread中創(chuàng)建畜挥;
(2)execute()方法也必須在UI Thread中調(diào)用仔粥;
(3)cancel()方法:取消該任務(wù),該方法只是將對應(yīng)的AsyncTask狀態(tài)標(biāo)記為cancel狀態(tài),并沒有真正取消異步操作蟹但;
(4)每個AsyncTask的execute()方法只能被執(zhí)行一次躯泰,否則多次調(diào)用時將會出現(xiàn)異常;
(5)重寫的四個方法是系統(tǒng)自動調(diào)用华糖,不能手動更改麦向;
如果存在理解偏差甚至錯誤的地方,請多多交流指正客叉!謝謝各位诵竭!
點擊下載源代碼:Android AsyncTask基礎(chǔ)