Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制,在Android中实现异步任务机制有两种方式,Handler和AsyncTask。Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制,AsyncTask,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。
public abstract class AsyncTask<Params, Progress, Result> {
有三个泛型参数:
三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。
一个异步任务的执行一般包括以下几个步骤:
1.execute(Params… params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。(手动调用)
2.onPreExecute(),在execute(Params… params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。(进度条初始化)
3.doInBackground(Params… params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress… values)来更新进度信息。
4.onProgressUpdate(Progress… values),在调用publishProgress(Progress… values)时,此方法被执行,直接将进度信息更新到UI组件上。
5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。
在使用的时候,有几点需要格外注意:
1.异步任务的实例必须在UI线程中创建。
2.execute(Params… params)方法必须在UI线程中调用。
3.不要手动调用onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法.(会自定执行)
4.不能在doInBackground(Params… params)中更改UI组件的信息。
5.一个任务实例只能执行一次,如果执行第二次将会抛出异常。
看一个例子
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
private ImageView imageView;
private Button button;
private String urlString = "http://images.china.cn/attachement/jpg/site1000/20150616/002564bb20a216e9add813.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView_main_show);
button = (Button) findViewById(R.id.button_main_download);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new DownloadImageTask(MainActivity.this).execute(urlString);
}
});
}
/*
* Params:输入任务的参数:doInBackground()方法的参数、execute()方法的参数
* Progress:任务执行是的进度指示:onProgressUpdate()方法的参数、publishProgress()方法的参数
* Result:任务执行的结果:doInBackground()方法的返回值、onPostExecute()方法的参数
*/
class DownloadImageTask extends AsyncTask<String, Integer, Bitmap> {
private ProgressDialog progressDialog;
private Context context;
public DownloadImageTask(Context context) {
this.context = context;
progressDialog = new ProgressDialog(context); progressDialog.setIcon(R.drawable.ic_launcher);
progressDialog.setTitle("提示信息");
progressDialog.setMessage("正在努力为您加载..."); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMax(100);
// 为Dialog添加取消监听
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// 取消异步任务
cancel(true);
}
});
}
// 该方法在主线程中调用
@Override
protected void onPreExecute() {
progressDialog.show();
super.onPreExecute();
}
// 该方法在子线程中调用
@Override
protected Bitmap doInBackground(String... params) {
InputStream inStream = null;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
try {
URL url = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setDoInput(true);
conn.setRequestMethod("GET");
conn.connect();
if (conn.getResponseCode() == 200) {
inStream = conn.getInputStream();
byte[] buffer = new byte[512];
int length = 0;
// 要下载文件的总长度
int fileLength = conn.getContentLength();
// 已经下载的文件长度
int downloadLength = 0;
while ((length = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, length);
// 计算的出当前的进度值
downloadLength += length;
int progress = (int) ((downloadLength / (float) fileLength) * 100);
// 发布进度,引起onProgressUpdate()方法的调用
publishProgress(progress);
if (isCancelled()) {
outStream = null;
break;
}
}
if (outStream != null) {
byte[] imgData = outStream.toByteArray();
Bitmap imgBitmap = BitmapFactory.decodeByteArray(imgData, 0, imgData.length);
return imgBitmap;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inStream != null) {
try {
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
// 该方法在主线程中调用
@Override
protected void onProgressUpdate(Integer... values) {
// 使用ProgressDialog显示进度值
progressDialog.setProgress(values[0]);
super.onProgressUpdate(values);
}
// 该方法在主线程中调用
@Override
protected void onPostExecute(Bitmap result) {
progressDialog.dismiss();
if(result != null) {
imageView.setImageBitmap(result);
} else {
Toast.makeText(MainActivity.this, "获取图片失败", Toast.LENGTH_SHORT).show();
}
super.onPostExecute(result);
}
// 该方法在主线程中调用
@Override
protected void onCancelled(Bitmap result) {
Toast.makeText(context, "取消了下载", Toast.LENGTH_SHORT).show();
super.onCancelled(result);
}
}
}
AsyncTask是对Thread+Handler良好的封装,在android.os.AsyncTask代码里仍然可以看到Thread和Handler的踪迹
我们可以看到关键几个步骤的方法都在其中,doInBackground(Params… params)是一个抽象方法,我们继承AsyncTask时必须覆写此方法;onPreExecute()、onProgressUpdate(Progress… values)、onPostExecute(Result result)、onCancelled()这几个方法体都是空的,我们需要的时候可以选择性的覆写它们;publishProgress(Progress… values)是final修饰的,不能覆写,只能去调用,我们一般会在doInBackground(Params… params)中调用此方法;另外,我们可以看到有一个Status的枚举类和getStatus()方法,Status枚举类代码段如下:
//初始状态
private volatile Status mStatus = Status.PENDING;
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
/**
* Returns the current status of this task.
*
* @return The current status.
*/
public final Status getStatus() {
return mStatus;
}
根据静态代码块的执行顺序,AsyncTask的初始状态为PENDING,代表待定状态,RUNNING代表执行状态,FINISHED代表结束状态,这几种状态在AsyncTask一次生命周期内的很多地方被使用,非常重要。
介绍完大纲视图相关内容之后,接下来,我们会从execute(Params… params)作为入口,重点分析一下AsyncTask的执行流程,我们来看一下execute(Params… params)方法的代码段:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
//如果该任务正在被执行则抛出异常
//值得一提的是,在调用cancel取消任务后,状态仍未RUNNING
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
//如果该任务已经执行完成则抛出异常
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
//改变状态为RUNNING
mStatus = Status.RUNNING;
//调用onPreExecute方法
onPreExecute();
mWorker.mParams = params;
sExecutor.execute(mFuture);
return this;
}
三个陌生的变量:mWorker、sExecutor、mFuture,我们也会看一下他们的庐山真面目:
关于sExecutor,它是java.util.concurrent.ThreadPoolExecutor的实例,用于管理线程的执行。代码如下:
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 10;
//新建一个队列用来存放线程
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
//新建一个线程工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
//新建一个线程
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//新建一个线程池执行器,用于管理线程的执行
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
mWorker实际上是AsyncTask的一个的抽象内部类的实现对象实例,它实现了Callable接口中的call()方法,代码如下:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
而mFuture实际上是java.util.concurrent.FutureTask的实例,下面是它的FutureTask类的相关信息:
* A cancellable asynchronous computation.
* ...
*/
public class FutureTask<V> implements RunnableFuture<V> { }
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
可以看到FutureTask是一个可以中途取消的用于异步计算的类。下面是mWorker和mFuture实例在AsyncTask中的体现:
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
//call方法被调用后,将设置优先级为后台级别,然后调用AsyncTask的doInBackground方法
public Result call() throws Exception {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return doInBackground(mParams);
}
};
//在mFuture实例中,将会调用mWorker做后台任务,完成后会调用done方法
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Message message;
Result result = null;
try {
result = get();
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
//发送取消任务的消息
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
//发送显示结果的消息
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
}
};
}
我们看到上面的代码中,mFuture实例对象的done()方法中,如果捕捉到了CancellationException类型的异常,则发送一条“MESSAGE_POST_CANCEL”的消息;如果顺利执行,则发送一条“MESSAGE_POST_RESULT”的消息,而消息都与一个sHandler对象关联。这个sHandler实例实际上是AsyncTask内部类InternalHandler的实例,而InternalHandler正是继承了Handler,下面我们来分析一下它的代码:
private static final int MESSAGE_POST_RESULT = 0x1; //显示结果
private static final int MESSAGE_POST_PROGRESS = 0x2; //更新进度
private static final int MESSAGE_POST_CANCEL = 0x3; //取消任务
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
//调用AsyncTask.finish方法
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//调用AsyncTask.onProgressUpdate方法
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_CANCEL:
//调用AsyncTask.onCancelled方法
result.mTask.onCancelled();
break;
}
}
}
我们看到,在处理消息时,遇到“MESSAGE_POST_RESULT”时,它会调用AsyncTask中的finish()方法,我们来看一下finish()方法的定义:
private void finish(Result result) {
if (isCancelled()) result = null;
onPostExecute(result); //调用onPostExecute显示结果
mStatus = Status.FINISHED; //改变状态为FINISHED
}
原来finish()方法是负责调用onPostExecute(Result result)方法显示结果并改变任务状态的啊。
概括来说,当我们调用execute(Params… params)方法后,execute方法会调用onPreExecute()方法,然后由ThreadPoolExecutor实例sExecutor执行一个FutureTask任务,这个过程中doInBackground(Params… params)将被调用,如果被开发者覆写的doInBackground(Params… params)方法中调用了publishProgress(Progress… values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress… values)方法将被调用;如果遇到异常,则发送一条MESSAGE_POST_CANCEL的消息,取消任务,sHandler处理消息时onCancelled()方法将被调用;如果执行成功,则发送一条MESSAGE_POST_RESULT的消息,显示结果,sHandler处理消息时onPostExecute(Result result)方法被调用。