为何要引入AsyncTask?
在Android程序开始运行的时候会单独启动一个进程,默认情况下所有这个程序操作都在这个进程中进行。一个Android程序默认情况下只有一个进程,但一个进程中可以有多个线程。
在这些线程中,有一个线程叫做UI线程(也叫Main Thread),除了UI线程外的线程都叫子线程(Worker Thread)。UI线程主要负责控制UI界面的显示、更新、交互等。因此,UI线程中的操作延迟越短越好(流畅)。把一些耗时的操作(网络请求、数据库操作、逻辑计算等)放到单独的线程,可以避免主线程阻塞。
Android给我们提供了一种轻量级的异步任务类AsyncTask。该类中实现异步操作,并提供接口反馈当前异步执行结果及进度,这些接口中有直接运行在主线程中的(如 onPostExecute,onPreExecute等)。
AsyncTask 使用
作为一个抽象类 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");
}
}
调用:
new DownloadFilesTask().execute(url1, url2, url3);
源码分析
首先我们从异步任务执行开始,即执行execute()开始分析,执行下面函数
//该方法运行在主线程
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
//将任务放到线程池中执行,线程池中线程都是子线程
return executeOnExecutor(sDefaultExecutor, params);//
}
随后我们就看executeOnExecutor(sDefaultExecutor, params) 执行的函数体是:
//该函数体运行在主线程
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case 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)");
}
}
mStatus = Status.RUNNING;
onPreExecute(); // 该方法是一个空方法体,并且是AsyncTask四个回调方法中第一个调用,运行在主线程
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
我们通过 exec.execute(mFuture)意思是在线程池执行一个“东西”,那 mFuture 这个“东西是什么尼?我们接下看下面:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//注意点 1
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);//调用AsyncTask四个回调方法中第二个调用,运行在子线程
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
//注意点 2
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
这里可以看出 mFuture 即是注意点2处,因为exec.execute(mFuture)是把mFuture放在子线程池中执行,那么注意点2 即是在子线程池中执行,那么其传入的参数mWorker也就是在子线程中执行,即注意点1也是在子线程中执行。
那么在doInBackground回调方法里调用publishProgress代码如下:
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
由上面代码知道,这个方法是运行在子线程里的,并通过handler (getHandler()实际上是得到Handler的一个子类即 InternalHandler代码如下)发送一个消息,在主线程处理消息:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
由上面的代码我们知道,publishProgress方法体里面的发送的消息在 handleMessage 的第二个case执行 即执行AsyncTask里面的onProgressUpdate 方法,而result.mData对应的即是publishProgress方法中参数列表传入的参数。
我们再看一下一开始的代码
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);//注意点1
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
由上面的代码知道注意点1 在doInBackground执行结束后才会执行,并且会执行下面的代码:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
如同publishProgress的代码,此处也会发送消息给主线程,
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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
result.mTask.finish(result.mData[0]); //执行这句代码
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
上面的代码之后会执行:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);/执行这句代码
}
mStatus = Status.FINISHED;
}
此处的onPostExecute执行参数列表的数据类型既是doInBackground函数返回的数据类型。
AsyncTask的机制原理
- 1、AsyncTask的本质是一个静态的线程池,AsyncTask派生的子类可以实现不同的异步任务,这些任务都是提交到
静态的线程池中执行。 - 2、线程池中的工作线程执行doInBackground()方法执行异步任务
- 3、当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数
注意
AsyncTask不适合大量数据的请求,因为AsyncTask中线程池一个时间只能执行一个,因为使用了同步锁;
- 由于Handler需要和主线程交互,而Handler又是内置于AsyncTask中,所有AsyncTask的创建必须在主线程。
- AsyncTaskResult的doInBackground(Params)方法执行异步任务运行在子线程中,其他方法运行在主线程中,可以操作UI组件。
- 不要手动的去调用AsyncTask的onPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute方法,这些都是由android系统自动调用的。
- 一个AsyncTask任务只能被执行一次。
- 运行中可以随时调用cancel(boolean)方法取消任务,如果成功调用isCancel()会返回true,并不会执行onPostExecute(),取而代之的是调用onCancelled()。从源码看,如果这个任务已经执行了这个时候调用cancel是不会真正的把task结束,而是继续执行,只不过改变的是执行之后的回调方法的onPostExecute还是onCancelled.
可能存在内存泄露情况,即非静态内部类持有外部类引用,解决办法同,Handler内存泄露解决办法一样,(在activity的onDestory 方法中调用 AsyncTask的cancel()方法)
并行或串行:在android 1.6之前的版本asynctask都是串行,即把任务放线程池中一串一串的执行,1.6到2.3改成并行,2.3之后为了维护系统稳定改成串行,但是任然可以执行并行操作。
- 可能会导致结果丢失
关于线程池中,
线程数目限制API19及以后:
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
线程数目限制API18及之前:
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE= 1;