本文将先阐述AsyncTask的概念、特征及基本用法,之后再以一个实例来验证实际的应用。
AsyncTask
AsyncTask -- 直接继承与Object类 在API-3中定义
一 概述:
AsyncTask 目的是为了更方便容易的使用UI 线程,它允许在UI线程中执行后台操作并将后台处理的结果返回给UI线程,而不需要繁琐的开启一个线程或者Handler来处理后台操作。
AsyncTask 是一个综合Thread 和 Handler的辅助类,并不是通用线程框架的一部分。AsyncTask 是短期后台操作的理想选择(最多几秒钟),如果你需要线程长时间的保持运行,强烈建议你使用 java.util.concurrent 包提供的各种API以满足你的需求,比如:Executor, ThreadPoolExecutor and FutureTask.
异步任务是指在后台运行的一些列运算,并将结果反馈给UI线程,异步任务有三个泛型输入:输入参数(param)、处理进度(Progress)、处理结果(results)。大致可分为四个步骤:预处理(onPreExecute)、后台处理(doInBackground)、更新处理进度(onProgressUpdate )、处理完成(onPostExecute)
PS:如果需要了解更多关于任务和线程的相关信息,可以阅读与进程和想成相关的开发指南
二 用法:
AsyncTask 是抽象类,所以必须继承才能使用。子类至少要重写doInBackground 这个方法,大多数情况也会重写onPostExecute,下面是一个继承AsyncTask的实例:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled())
break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
只要子类建立好了之后,运行这个DownloadFilesTask非常容易:
new DownloadFilesTask().execute(url1, url2, url3);
AsyncTask的三个泛型
一个异步任务使用到的泛型如下:
1.Param ,用于指定任务执行前的参数类型
2.Progress, 用于指定后台计算任务的百分比数值类型
3.Result,用于指定后台计算结果的数据类型
PS:并不是所有的泛型都需要指定,如果不需要使用泛型可以使用void标识:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
AsyncTask 中的四个步骤:
1.onPreExecute() -->在UI线程中执行(启动AsyncTask时调用,无需在程序中显示的调用该API),这步主要是完成一些基本的设置行为,比如(为后台处理进度)实例化一个UI界面
2.doInBackground(Params...)--> 由后台线程调用,当执行完onPreExecute 后会立即执行该API,此步骤主要是用于执行一些后台的计算行为,启动AsyncTask时所带的参数将传递给该API,此API return的参数将传递给最后一步(onPostExecute),在这一步的时候可以调用 publishProgress(Progress...)将数据传递给onProgressUpdate以告知当前的处理进度。
3.onProgressUpdate(Progress...) --> 在UI线程中执行,当收到来自 publishProgress(Progress...)的消息后,可以根据需求更新UI界面上的动画来告知用户当前的处理进度。
4.onPostExecute(Result)--> 在UI线程中调用(非显示的调用,由线程自动完成)处理的结果会被传递到此API,可用于更新处理的结果
取消任务:
AsyncTask 在运行的过程中也可以调用AsyncTask对象的cancel(booean)方法取消当前的任务,当调用此方法后,会等待doInBackground(Object[]) 执行完成,后再调用 onCancelled(Object)方法而不会再调用onPostExecute(Object)。如果主线程需要尽快的检查当前的任务是否被取消可以使用isCancelled() 做周期性的检查
PS:cancel的入参可以决定是否终端后台任务线程,后台任务线程捕获到中断异常之后会决定如何处理。
内存可观察性:
AsyncTask 中所有的回调都是同步的,以保证线程的安全性。如下面的使用方式无需使用synchronizations即可保证线程安全:
·在constructor或者onPreExecute中设置类属性,然后在doInBackground中引用这些属性
·在doInBackground中设置类属性,然后在onProgressUpdate和onPostExecute中引用那个这些属性。
执行顺序:
AsyncTask在第一次发布的时候,是在后台的一个单一线程中串行执行的。从DONUT(android 1.6)开始,AsyncTask被放在线程池中被允许并发执行。从Android 3.0/3.1/3.2 Honeycomb(蜂巢)版本后,为了避免并发执行带来的常见的应用程序错误。
如果你确实需要并发执行,你可以将THREAD_POOL_EXECUTOR作为参数传入executeOnExecutor(java.util.concurrent.Executor, Object[])来执行AsyncTask。
三、应用实例
1.界面布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/TextView01"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/ProgressBar02"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/Button_Start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Update progressbar" />
<Button
android:id="@+id/Button_Stop"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stop update" />
</LinearLayout>
2.Activity
package com.sunmedia.androidasynctask;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private ProgressBar progressBar;
private Button btStart;
private Button btStop;
private TextView textView;
ProgressBarAsyncTask mAsyncTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btStart = (Button)findViewById(R.id.Button_Start);
btStop = (Button)findViewById(R.id.Button_Stop);
progressBar = (ProgressBar)findViewById(R.id.ProgressBar02);
textView = (TextView)findViewById(R.id.TextView01);
btStart.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mAsyncTask = new ProgressBarAsyncTask(textView, progressBar);
mAsyncTask.execute(1000);
}
});
btStop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("test", "btStop onclick");
if(mAsyncTask != null){
boolean b = mAsyncTask.cancel(false);
System.out.println(b);
}
}
});
}
}
3.AsyncTask
package com.sunmedia.androidasynctask;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* 生成该类的对象,并调用execute方法之后 首先执行的是onProExecute方法 其次执行doInBackgroup方法
*
*/
public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
private TextView textView;
private ProgressBar progressBar;
public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
super();
this.textView = textView;
this.progressBar = progressBar;
}
/**
* 这里的Integer参数类型对应AsyncTask中的第一个泛型参数 这里的String返回值对应AsyncTask的第三个泛型参数
* 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
* 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
*/
@Override
protected String doInBackground(Integer... params) {
CalOperator calOperator = new CalOperator();
int i = 0;
for (i = 10; i <= 100; i += 10) {
Log.d("test", "doInBackground,name:"
+ Thread.currentThread().getName());
if (calOperator.operator() != -1) {
publishProgress(i);
} else {
break;
}
}
return i + params[0].intValue() + "";
}
/**
* 这里的String参数类型对应AsyncTask中的第三个泛型参数
* 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
*/
@Override
protected void onPostExecute(String result) {
Log.d("test", "onPostExecute,name:" + Thread.currentThread().getName());
textView.setText("异步操作执行结束" + result);
}
// 该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
@Override
protected void onPreExecute() {
Log.d("test", "onPreExecute,name:" + Thread.currentThread().getName());
textView.setText("开始执行异步线程");
}
/**
* 这里的Intege参数类型对应AsyncTask中的第二个泛型参数
* 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
* onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
*/
@Override
protected void onProgressUpdate(Integer... values) {
Log.d("test", "onProgressUpdate,name:"
+ Thread.currentThread().getName());
int vlaue = values[0];
progressBar.setProgress(vlaue);
}
@Override
protected void onCancelled() {
Log.d("test", "onCancelled,name:" + Thread.currentThread().getName());
super.onCancelled();
}
}
4.模拟实际任务
package com.sunmedia.androidasynctask;
//模拟计算环境
public class CalOperator {
public int operator(){
int i = 0;
try {
//休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//捕获到中断异常后退出
i = -1;
}
return i;
}
}