Android UI操作是线程不安全的,若想在子线程红进行UI操作,需要借助Android提供的Handler。Android提供的AsyncTask其实是对Handler的封装,方便我们在子线程中更新UI元素。AsyncTask是一个泛型类,书写格式为: AsyncTask。这三个参数的用途如下:
1. Params
这三个参数的用途如下:
1. Params
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
2. Progress
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
3. Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
Android SDK中给出的一个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
AsyncTask是个抽象类,有四个方法需要被重写,这四个方法的的调用过程如下:
第1步. onPreExecute()方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作。
第2步. doInBackground(Params...)方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回。如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
第3步. 当在后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate(Progress...)方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
第4步. 当后台任务执行完毕并通过return语句进行返回时,onPostExecute(Result)很快会被调用。此处可以
进行UI操作。
AsyncTask的源码
当初使用AsyncTask的时候,一直不明白onProcessUpdate为何会定期被调用。后来在工作中要实现一个定时更新任务进度的功能,我们用的是一个Handler给自己发送消息来进行定时刷新。现在研究了一下AsyncTask的实现原理,发现它也是使用Handler给自己发送消息的原理来实现定时刷新。当我选用的是Android 4.4的源码,若大家能搞懂AsyncTask的实现原理,也能自己写一个简单的AsyncTask。
首先看看构造函数:注意,构造函数一定要在UI中调用。
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask(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);
}
}
};
}这个函数初试化了两个变量:mWorker和mFuture。mWorker是一个Callable对象,mFuture是一个实现了Runnable和Future的对象。启动AsyncTask调用的是execute(Params)public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}该方法调用了executeOnExecutor()方法,代码如下:
public final AsyncTask 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;
}该方法先设置运行状态,然后调用onPreExecute,最后调用exec.execute(mFuture).传入的exec是sDefaultExecutor, sDefaultExecutor是一个SerialExecutor对象,源代码如下:
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);
}
}
}
当调用sDefaultExecutor.execute(runnable),该方法用一个ArrayDeque来保存runnable,runnable会在合适的时候被调用。第一次运行mActivity等于null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。这里使用了一个try finally代码块,并在finally中调用了scheduleNext()方法,每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。
sDefaultExecutor.execute(Runnable)传入的对象是mFuture,mFuture是一个FutureTask对象,其构造函数如下所示:
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;// ensure visibility of callable
}由AsyncTask的构造函数可知,FutureTask中的callable是mWorkder对象。FutureTask的run()代码如下所示:public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}因此,最终会调用mWorker的call()。mWorker的call()位于AsyncTask的构造函数中,大家可以发现其先调用doInBackgroud(Params),然后调用postResult().postResult(Result)的源代码如下:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}sHandler是一个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
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
当传入的是一条MESSAGE_POST_RESULT消息,就会去执行AsyncTask.finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。当我们在doInBackgroud(Params)中利用for循环来调用publishProgress(Progress... values),就会模拟出
定时调用onProgressUpdate(Progress... values)的效果。publishProgress(Progress... values)的源码如下:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult(this, values)).sendToTarget();
}
}
它首先会判断线程是否已经结束,若没结束,则sHandler给自己发送一个消息,消息的类型为MESSAGE_POST_PROGRESS,sHandler.handleMessage(Message)则会调用onProgressUpdate(Progress... values)。
由上可知sDefaultExecutor先调用mFuture的run(), 而mFuture的run()又会调用mWorker的call()。当执行完一个任务后,mFuture的done()会被回调。done()会调用ppostResultIfNotInvoked(Result result),源码如下:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}postResult(Result)已经在前面有过讲解,在此不再赘述。