Android AsyncTask 从源码角度解析其原理和用法
来源:程序员人生 发布时间:2015-06-16 08:15:26 阅读次数:2538次
AsyncTask 在线查看源码地址:AsyncTask源码查看
AsyncTask简介:
尽人皆知知道,Android UI是线程不安全的,如果要想在子线程中更新UI操作,必须使用Android的异步消息处理机制。固然我们自己可以实现1个Handler+Message消息处理机制来在子线程中更新UI操作。有时候觉得自己写这个异步消息处理机制很麻烦有木有??不过庆幸的是,Android 给我们实现了这么1套异步消息处理机制,我们直接拿来用就是了,从而
AsyncTask就诞生了。AsyncTask用于Android的异步消息 处理机制,来实现子线程和UI线程间的1些通讯。
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);
由于AsyncTask是个抽象类,如果我们要使用它,必须写个子类去继承AsyncTask,在继承时我们可以为AsyncTask类指定3个泛型参数,这3个参数的用处以下:
1.Params:
用于 doInBackground()后台履行任务的参数。
2.Progress:
用于后台履行任务进度的更新。
3.Result:
当任务履行终了时,需要对结果进行返回,用于保存履行结果。
AsyncTask履行的步骤以下:
onPreExecute():在UI线程中履行,通经常使用于异步任务履行之前,初始化1些数据,比如:初始化进度条等。
doInBackground(java.lang.Object[]):在后台线程中履行,1般耗时任务都在这个方法里面实现,固然在这个方法里面我们必须返回履行结果,用于onPostExecute(java.lang.Object)步骤,将异步任务结果发布到UI线程中。在这个方法里我们可以选择性的调用publishProgress(java.lang.Object[])方法,用于将异步任务履行进度信息发布到UI线程中。
onProgressUpdate(java.lang.Object[]):在UI线程中履行,用于对后台任务履行进度信息的更新。只有在doInBackground(java.lang.Object[])方法中调用了publishProgress(java.lang.Object[])方法,才能拿到后台任务履行进度信息。
onPostExecute(java.lang.Object):在UI线程中履行,用于获得后台任务履行的结果。
AsyncTask的特点:
1.异步任务的实例必须在UI线程中创建
2.execute(Params... params)方法履行必须在UI线程中调用
3.1个异步任务只能履行1个,不然会抛出“Cannot execute task:the task is already running.”异常。
4.不能在doInBackground 做更新UI的操作。
5.不要手动的去调用onPreExecute(),doInBackground(Params...
params),onProgressUpdate(Progress... values),onPostExecute(Result result)
这几个方法。
解析以上5点Why??等我们分析完全个源码再来回答这个问题吧。
AsycnTask的源码分析:
1.execute(params)
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
这个方法只履行了1行代码,就是调用executeOnExecutor(sDefaultExecutor, params) 方法。进去看看:
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();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
我们可以看到这里有个状态值的判断,就是枚举类型Status了,看看代码:
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,
}
3种状态用来标志当前的异步任务处于那种状态。当前任务的初始状态是PENDING:为待履行状态。
接着我们回到executeOnExecutor方法中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();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
第1个任务履行,第 3 行的if条件不满足,那末第15行重新标记当前任务状态为正在履行。第17行调用 onPreExcute()方法,在UI线程中我们可以重写此方法用于履行异步任务之前的1些准备工作。第20行是exec线程池真正履行后台耗时任务mFuture的地方。mFuture就是实例化AsyncTask对象的时候取得。exec是外部传入的参数,该参数在实例化的时候默许是sDefaultExecutor顺序履行的线程池。
看代码你会发现,当同1个任务被履行两次的话,第 3 行的if条件就满足了,自然就会抛出相应的异常,这里就解释了AsyncTask特点的第3点,同1个任务不能被履行两次以上。
2.我们来看看AsyncTask实例化的代码
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
看着代码这么长,其实就初始化了两个变量,第1个变量是mWorker,第2个变量mFuture。
1.mWorker:是1个内部接口类对象,实现了里面的call()方法,call()方法里调用了doInBackground(mParams)方法。什么时候调用mWorker.call()方法?我们发现 mWorker作为参数用作第2个变量mFuture的获得。
2.mFuture:是1个线程接口类对象,run()方法里面调用了mWorker.call()方法,具体怎样调用就不属于AsyncTask类的范围了。</span>
总结:其实实例化对象的时候只要记住我们持有了mFuture对象,里面回调了done()方法,回调里面仅仅是调用了postResultIfNotInvoked(get())方法。进入看看</span>
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
代码10⑴2行可以看出,这里用Handler+Message消息处理机制将 异步任务履行结果发布到UI线程中,看看sHandler怎样实现的吧?
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
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
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
这是1个静态内部类,在取得不同消息做不同处理,MESSAGE_POST_RESULT:消息调用 mTask.finish()。代码在第 8 行实现了,该方法里面调用了
onPostExecute(result)
将异步履行任务发布到UI线程中,并且在第 14 行改变当前任务的履行状态为finished。 MESSAGE_POST_PROGRESS:消息调用了 mTask.onProgressUpdate().用于发布任务履行进度到UI线程中。
这里我们可以看出,AsyncTask 自己内部实现了1个 内部的 Handler + Message + Thread 消息处理机制用于履行耗时任务更新UI操作等。 我们会发现InternalHandler的申明实在类加载的时候就实现了,代码:
private static final InternalHandler sHandler = new InternalHandler();
由于我们的Handler+Message消息处理机制是对用于UI线程的通知,基于消息是谁发送谁处理原理,我们的Handler必须是在UI线程中申明,也就是说,只有UI线程持有Handler的对象发送的消息才可以在UI线程中获得到这个消息。因此我们就能够解释
AsyncTask特点的第1点:异步任务的实例必须在UI线程中创建。
至此 AsyncTask履行基本结束。
3.总结: 客户端调用execute()方法,先履行 onPreExecute()方法,然后通过线程池sDefaultExecutor 履行1个 mFuture异步任务,在这个进程中 调用doBackground方法履行后天任务,然后通过InternalHandler 消息处理机制将后台履行的结果通过 onPostExecute()方法发布给UI线程。由于onPreExecute()方法的运行是在后台任务履行前运行,所以 execute()必须在 UI线程中调用。这就解释了 AsyncTask特点的第2点:execute(Params...
params)方法履行必须在UI线程中调用。
注意:我么这里还有1个问题:最新版本的AsyncTask 是顺序履行异步任务的,也就是说只有第1个任务履行完成以后才能履行第2条任务,为何会这样呢?还是从源码中找答案吧。AsyncTask 使用的默许线程池是 sDefaultExecutor,看代码实现:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static final InternalHandler sHandler = new InternalHandler();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
private volatile Status mStatus = Status.PENDING;
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
AsyncTask 内部实现了1个顺序履行的线程池,那末如果我们想要并发履行异步任务该怎样办呢?不用着急, AsyncTask 给你留了1个后门,我们使用 AsyncTask 1般都是调用 execute(params)方法, 其实你会发现 execute 也是调用 executeOnExecutor方法的,只是参数使用的是默许sDefaultExecutor,所以我们可以 自己直接调用executeOnExecutor方法,然后自定义1个 并发的线程池作为参数传入。比例我们可以这么调用:
ExecutorService executorService = Executors.newCachedThreadPool();
asyncTask.executeOnExecutor(executorService,params);
那末这个任务就是并发的了。
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠