AsyncTask相信或多或少都使用过,其封装了线程池和handler,更优雅的处理了异步任务的交互,用起来也极为简单
本篇大概会涉及:
- asynctask为何不能在子线程execute
- asynctask为何不能执行两遍execute
- asynctask的cancel如何停止一个正在执行的线程
- 我们最常用的几个方法,在asynctask中何处被调用,如何被调用
- asynctask的整个执行逻辑
简单使用:
private class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
mDialog.show();
}
@Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
mMyAsyncTask.cancel(true);
mDialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(Void result) {
mDialog.dismiss();
mTextView.setText("加载完毕");
//MyAsyncTask myAsyncTask = new MyAsyncTask(); 这里加上 就会循环执行
//myAsyncTask.execute();
}
@Override
protected void onCancelled() {
Log.e(TAG, Thread.currentThread().getName() + " onCancelled ");
}
}
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
使用注意:不可以在子线程调用myAsyncTask.execute();且同一个任务只可以执行一次(同一个任务指的是new MyAsyncTask())
太简单了,都不好意思说怎么用的,简单说下这泛型中的三个参数:
Params, Progress, Result
Params是doInBackground的参数类型,是execute执行时传递的参数类型
Progress 是onProgressUpdate的参数类型
Result 是onPostExecute任务结束返回的参数类型
----------------------不华丽的风格陷---------------------------------
看源码解析吧:
我们基于myAsyncTask.execute();这行代码分析;
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//必须是未执行的状态也就是创建asyncTask默认的状态
//如果是正在执行的任务或者是已经执行的任务都会直接抛出异常
//这里就验证了 一个执行中的任务不可以再次执行
//一个已经执行的任务(指的是new MyAsyncTask())不可以再次执行
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 通常我们会实现这个protected来做一些准备工作
//比如ui的更新 此时还是在主线程中
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
那么 mWorker exec mFuture分别都是什么?
mWorker:实现了Callable其call方法主要功能是调用doInBackground方法并获取结果,并将结果通过Handler切换到主线程
mFuture:实现了Runnable 其run方法会在子线程调用mWorker的call方法
exec:通过线程池执行mFuture
为什么没有展示一下他们的源码呢,下面会按流程分析到
看下exec,其传入的是一个默认sDefaultExecutor,最终来到这里 看下execute做了啥
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);
}
}
}
就是将一个runnable加入到队列中,这个r我们前面也说了就是mFuture下面的函数就是通过线程池执行这个runnable,好像没有什么逻辑这里,那么我们再去看下mFuture的run做了什么
public void run() {
result = c.call();
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);
}
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // 释放占用的cpu资源 让给其他的任务 但放弃的时间不确定
yield:释放占用的cpu资源 让给其他的任务 但放弃的时间不确定,这里不深究多线程的东西,继续走我们的主线
为了避免篇幅过长,我们只关注核心方法,首先调用了c.call();拿到了结果,那么我们肯定有个疑问,这个c是什么鬼?ok,我们看下c在哪里赋值的,
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//asyncTask的构造函数中
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);
}
}
};
看到这里明白了,c是mWorker,那么我们知道了,真正获取结果还是在mWorker的call方法中啊,再去看一下,他的位置在asyncTask的构造函数中
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);
}
return result;
}
};
看到一个属性的方法doInBackground,是不是突然觉得遇到熟悉的东西了…这里就调用我们自己的实现了,doInBackground(mParams)是运行在子线程的,这里我们模拟一下数据立即就返回了,那么会调用postResult(result);我们跟着看一下他的实现
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
这个应该也是非常熟悉的了,看到这个直接找handleMessage实现就稳了
@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那么直接去看下finish就行,mTask也就是this
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果没有调用cancel那么直接调用onPostExecute了,ok这里我们一次任务执行差不多结束了,但是其mFuture的done还没有执行,可能有疑惑了,这哥们哪里调用的?
public void run() {
result = c.call();
if (ran)
set(result);
}
回到mFuture的run方法,在我们拿到数据后会调用 set(result); 在finishCompletion方法中我们可以看到 done();的调用
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);
}
}
};
回到done方法 看下他的实现做了啥呢?
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
这里根据wasTaskInvoked判断是否调用postResult,但是在mWorker的call方法中我们已经将其置为true了,所以这里就GG了
走到这里我们认为其一次任务执行完毕,但是还有一个经常用到的publishProgress方法也要讲一下,这哥们怎么用的?
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
其也是运行在子线程的,这个消息我们上面也看到过
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;
}
}
又遇到我们熟悉的onProgressUpdate方法了,我们经常在doInBackground执行耗时任务的同时通过publishProgress来触发onProgressUpdate来做ui更新,原来就是这样实现的啊…
还有个cancal方法需要讲一下 cancel(true);
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
U.compareAndSwapInt(this, STATE, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
t.interrupt();这里中断了线程的执行,call结束后通过handler处理时发现这个任务已经被取消 那么就会调用onCancelled 我们就可以通过实现onCancelled来完成一些ui交互 因为此时又在主线程了
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
@SuppressWarnings({"UnusedParameters"})
protected void onCancelled(Result result) {
onCancelled();
}
经过我测试发现,就算调用了cancel,doInBackground中的任务还是会执行完,仅仅是不会调用onPostExecute罢了,而是走onCancelled,学过Java多线程的可能都知道通过interrupt来停止线程是停止不了的,需要辅以抛异常的方法,或者使用interrupted判断和retrun结合来停止线程,但是在run方法中貌似都没有这些,这里暂时我还没想到为什么停止了一个没有停止的线程有何意义,知道的烦请流个言,我是通过调用cancel辅以在doInBackground中catch捕获InterruptedException 异常直接返回的方式来结束线程运行,看下log验证
@Override
protected void onProgressUpdate(Integer... values) {
mMyAsyncTask.cancel(true);
mDialog.setProgress(values[0]);
}
@Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(80);
} catch (InterruptedException e) {
Log.d(TAG, "InterruptedException : ");
return null;
}
publishProgress(i);
Log.d(TAG, "doInBackground : " + i);
}
Log.d(TAG, "doInBackground : 结束");
return null;
}
如果注释了return null;日志如下,很明显,虽然调用了cancel,他也确实调用了interrupt,但是其线程并没有停止
.........
doInBackground : 91
doInBackground : 92
doInBackground : 93
doInBackground : 94
doInBackground : 95
doInBackground : 96
doInBackground : 97
doInBackground : 98
doInBackground : 99
doInBackground : 结束
main onCancelled
如果解开return null的注释:很明显线程立即就停止执行并调用了onCancelled方法
doInBackground : 0
InterruptedException :
main onCancelled
总结:THREAD_POOL_EXECUTOR(线程池) Future Worker AsyncTask
1.线程池只负责执行runnable(Future),
2. 而Future则负责在子线程中调用Callable(Worker),并提供了一些其他操作方法,比如cancel方法,可以中断正在执行的线程
3.worker(Callable)只需要在call中接收doInBackground返回的结果即可,
4.AsyncTask统领全局,根据任务是否取消的状态来决定调用onPostExecute亦或onCancelled,当然AsyncTask还做了其他很多的工作,
--------四者分工明确
碎片时间文章不太工整,有错误的地方请指出