用于執(zhí)行異步任務(wù)
AsyncTask<Params,Progress,Result>是一個(gè)抽象類,通常用于被繼承
需要指定以下三個(gè)泛型參數(shù):
Params:啟動(dòng)任務(wù)時(shí)輸入?yún)?shù)的類型
Progress:后臺(tái)任務(wù)執(zhí)行中返回進(jìn)度值的類型
Result:后臺(tái)執(zhí)行任務(wù)完成后返回結(jié)果的類型
耗時(shí)操作:如加載網(wǎng)絡(luò)圖片等需要等待的操作,通常不允許放進(jìn)主線程中
回調(diào)方法:
doInBackground:必須重寫,異步執(zhí)行后臺(tái)線程將要完成的任務(wù),所有耗時(shí)操作都將要在這個(gè)方法內(nèi)執(zhí)行
onPreExecute:執(zhí)行后臺(tái)耗時(shí)操作前被調(diào)用,通常用于完成一些初始化操作
onPostExecute:當(dāng)doInBackground()完成后系統(tǒng)會(huì)自動(dòng)調(diào)用onPostExecute()方法,并將doInBackground()的值傳給該方法
onProgressUpdate():在doInBackground()方法中調(diào)用publishProgress()方法更新任務(wù)執(zhí)行進(jìn)度后就會(huì)觸發(fā)該方法
回調(diào)方法調(diào)用順序:
onPreExecute()-->doInBackground()-->onProgressUpdate()-->onPostExecute()
通過(guò)一個(gè)例子也加深了對(duì)AsyncTask的理解:
加載一張網(wǎng)絡(luò)圖片
作為一個(gè)不穩(wěn)定的耗時(shí)操作,是嚴(yán)禁被放入主線程中的,所以需要在異步處理中下載圖像,在UI線程中設(shè)置圖像
過(guò)程:
1.創(chuàng)建一個(gè)布局文件,并創(chuàng)建一個(gè)imageView組件和一個(gè)progressBar組件
image.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
注意progressBar 使用android:visibility="gone"設(shè)置其默認(rèn)不顯示出來(lái)
android:layout_centerInParent="true"設(shè)置其居中顯示
2.創(chuàng)建ImageTest類,并創(chuàng)建內(nèi)部類myAsyncTask
public class ImageTest extends Activity {
private ImageView mImageView;
private ProgressBar mProgressBar;
private static String URL="http://img1.gtimg.com/digi/pics/hv1/109/199/2039/132636829.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
mImageView=(ImageView)findViewById(R.id.imageview);
mProgressBar=(ProgressBar)findViewById(R.id.progressbar);
new myAsyncTask().execute(URL);
}
class myAsyncTask extends AsyncTask<String,Void,Bitmap>{
@Override
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap); //操作UI設(shè)置圖像
mProgressBar.setVisibility(View.GONE);
mImageView.setImageBitmap(bitmap);
}
@Override //傳入可變長(zhǎng)數(shù)組
protected Bitmap doInBackground(String... params) {
//獲取傳遞進(jìn)來(lái)的參數(shù)
String url=params[0];
Bitmap bitmap=null;
URLConnection connection;
InputStream is;
try {
connection=new URL(url).openConnection();
is=connection.getInputStream();//獲取輸入流
BufferedInputStream bis=new BufferedInputStream(is);
Thread.sleep(3000); bitmap=
BitmapFactory.decodeStream(bis);//將輸入流解析成bitmap
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return bitmap;
}
}
}
其中注意:
1.傳入U(xiǎn)RL為String類型,表示需要加載的圖片地址
protected Bitmap doInBackground(String... params) {
String url=params[0];
doInBackground()方法傳遞進(jìn)來(lái)的數(shù)組是一個(gè)可變長(zhǎng)數(shù)組,也就是說(shuō)可以傳遞不只一個(gè)參數(shù),由于本例子只傳入一個(gè)參數(shù),所以只需要取出第一位即可,即params[0]
connection=new URL(url).openConnection();
這里獲取網(wǎng)絡(luò)連接對(duì)象
is=connection.getInputStream();
這里獲取輸入流
BufferedInputStream bis=new BufferedInputStream(is);
//將輸入流包裝
bitmap= BitmapFactory.decodeStream(bis);
將輸入流解析成bitmap
6.在線程開始之前需要先把progressBar設(shè)置成可見
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setVisibility(View.VISIBLE);}
protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); mProgressBar.setVisibility(View.GONE); mImageView.setImageBitmap(bitmap);}
doInBackground()方法執(zhí)行后返回一個(gè)bitmap圖像,獲取到圖像后就可以操作UI設(shè)置圖像,同時(shí)將progressBar隱藏
new myAsyncTask().execute(URL);
設(shè)置傳遞進(jìn)去的參數(shù),首先調(diào)用onPreExecute()方法,接下來(lái)調(diào)用doInBackground()方法,最后調(diào)用onPostExecute()方法
9.最后補(bǔ)充Mainfest文件,并添加activity和網(wǎng)絡(luò)權(quán)限
<uses-permission android:name="android.permission.INTERNET"/>
再在主線程中添加按鈕事件
10.若覺得圖片加載太快看不見progressBar可以添加Thread.sleep(3000)讓其睡眠3秒再開始加載
onPreExecute()加載進(jìn)度條
doInBackground()下載網(wǎng)絡(luò)數(shù)據(jù)(耗時(shí)操作)
onPostExecute()顯示圖片
下面用ProgressBar來(lái)模擬進(jìn)度條
首先創(chuàng)建布局文件并設(shè)置progressBar樣式
progressbar.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/pg"
style="?android:attr/progressBarStyleHorizontal"
android:layout_gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
然后創(chuàng)建ProgressBarTest類
public class ProgressBarTest extends Activity {
ProgressBar mProgressBar;
private myAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
mProgressBar=(ProgressBar)findViewById(R.id.pg);
mTask=new myAsyncTask();
mTask.execute();
}
class myAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... params) {
for(int i=0;i<100;i++){
publishProgress(i);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressBar.setProgress(values[0]);
}
}
}
其中
protected Void doInBackground(Void... params) {
for(int i=0;i<100;i++){
publishProgress(i);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace(); } }
return null;}
publishProgress(i);用于調(diào)用更新進(jìn)度的函數(shù),所以將i傳入
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressBar.setProgress(values[0]);
}
將values即傳入的i取出并設(shè)置給progressBar
可是運(yùn)行時(shí),若退出再進(jìn)入頁(yè)面,progressBar會(huì)等一段時(shí)間再開始動(dòng),這是因?yàn)楸仨毜鹊角懊娴膖ask執(zhí)行完成后才能執(zhí)行后面的操作,所以要等到for循環(huán)執(zhí)行完畢后才能執(zhí)行下一次循環(huán)
解決方法:使Activity的生命周期和AsyncTask的生命周期保持一致即可
添加onPause()方法
@Override
//當(dāng)mTask線程不為空而且狀態(tài)是Running
protected void onPause() {
super.onPause();
if(mTask!=null&&
mTask.getStatus() == AsyncTask.Status.RUNNING){
mTask.cancel(true);
}}
判斷當(dāng)mTask線程不為空而且狀態(tài)是Running時(shí),取消線程
可是運(yùn)行后還是沒解決,這是因?yàn)閏ancel只是將對(duì)應(yīng)的AsyncTask標(biāo)記為Cancel狀態(tài),并不是真正的取消線程的執(zhí)行
而且在java中也是不能直接停止線程的,必須等一個(gè)線程執(zhí)行完畢后才能執(zhí)行后面的操作
所以應(yīng)該在具體的異步線程中去檢測(cè)狀態(tài)值的改變,一旦當(dāng)前AsyncTask的狀態(tài)改為cancel,就跳出循環(huán)來(lái)結(jié)束掉操作,從而結(jié)束掉整個(gè)線程的邏輯
在doInBackground()方法中加入判斷
protected Void doInBackground(Void... params) {
for(int i=0;i<100;i++){
if(isCancelled()){
break;
}
publishProgress(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); } }
return null;}
同理在更新操作中也要加入判斷
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (isCancelled()){
return;
}
mProgressBar.setProgress(values[0]); }}
總結(jié)
AsyncTask使用注意事項(xiàng):
-必須在UI線程(onCreate())中創(chuàng)建AsyncTask實(shí)例
-必須在UI線程(onCreate())中調(diào)用AsyncTask的execute()方法
-重寫的四個(gè)方法時(shí)系統(tǒng)自動(dòng)調(diào)用的,不能手動(dòng)調(diào)用
-每個(gè)AsyncTask只能被執(zhí)行一次,多次調(diào)用會(huì)引起異常
-在AsyncTask中只有doInBackground()運(yùn)行在其他線程,其他方法都運(yùn)行在主線程,也就是說(shuō)在其他方法中可以更新UI,而唯一在這個(gè)方法中需要做異步處理,不能再此方法中更新UI,安卓提供了onPreExecute()和onPostExecute()去承接異步處理中的操作去更新UI