1.AsyncTask的使用
AsyncTask使用分为三步:
① 创建一个类来继承AsyncTask
②创建一个这个类的实例对象
③使用这个实例对象的execute方法
//第一个,创建一个类继承AsyncTask,记得一定要是静态类,否则会出现内存泄漏
static class Asyncktask extends AsyncTask{
@Override
protected Void doInBackground(Void... voids) {
return null;
}
}
//第一个Void参数类型为Params类型参数,即你传入的参数,第二个参数为Progress类型,即进度的类型
//第三个为Resault类型参数,即返回结果参数类型。
//第二步,创建这个类的实例
AsyncTask asyncTask = new Asyncktask();
//第三步,调用这个类的execute方法
asyncTask.execute()
这是AsyncTask的使用方法,是不是很简单?AsyncTask是用来干嘛的呢?相信能点进来看的各位都知道,但是为了自己记得更牢固一点,还是写一下,因为在主线程中不能干耗时任务,否则会造成ANR(主线程超过5s会出现),因此,一些耗时的操作必须要放到工作线程中去实现,这也就是AsyncTask出现的目的,AsyncTask内部封装了Handler用来切换子线程以及主线程,封装了线程池来进行耗时任务的执行,因此AsyncTask就是为我们封装好的用来执行单个的耗时任务的工具,为什么是单个呢?因为AsyncTask的excute方法只能够执行一次。
ok,既然知道了它是干嘛的,为了更好的了解它,我们还要知道它是怎么办到的。
首先我们进入到AsyncTask的构造函数。
//无参构造函数调用了下面的有参构造函数
public AsyncTask() {
this((Looper) null);
}
//这是实际调用的构造函数
public AsyncTask(@Nullable Looper callbackLooper) {
//这里把主线程的Handler赋值给AsyncTask的Handler,这样就能够通过
//这个Handler向主线程发送消息,更新数据。
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//这里先不看这个,之后会讲到,这个WorkerRunnable是继承callable接口,作为下面FutureTask的构造参数,之后会执行这个call方法
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
//设置线程的优先级为后台
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//将doInBackground的返回值取出
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
//调用postResult方法
postResult(result);
}
return result;
}
};
//这个FutureTask实际上是继承了Runnable的,因此可以作为线程池执行的参数去执行
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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
第一步,初始化实例对象完成了什么功能呢?首先,它将主线程的Handler赋给了自身的handler,这样就可以切换主线程和子线程,其次,初始化了一个FutureTask对象,它继承了Runnable,这里做了一些封装,将WorkerRunnable对象封装到FutureTask对象中。
之后我们看excute方法,这个方法又做了什么呢?我们点进去来看它的源码。
//这里标注了必须要在主线程执行
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
//调用了executeOnExecutor()方法
@MainThread
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;
//这里就去执行了mFuture这个runnable,会调用exec的execute方法。
exec.execute(mFuture);
return this;
}
在代码里可以看到,首先判断了是不是运行过,运行过就会把mStaus置为相应的类型(Status枚举类),因此这也就是AsyncTask的execute方法只能运行一次的原因。
之后会调用第一个回调方法,onPreExecute方法,这也证明了,第一个调用的方法为onPreExecute方法。
之后会exec的execute方法,这个exec是什么呢?我们看一下。
//exec是一个继承了Executor类型的SerialExecutor类,调用它的execute方法就是调用下面的execute方法
private static class SerialExecutor implements Executor {
//这是一个没有容量限制的双向队列
final ArrayDeque mTasks = new ArrayDeque();
//mActive是正在执行的Runnable
Runnable mActive;
public synchronized void execute(final Runnable r) {
//这里做了一层封装,用new Runnable 封装了传递过来的runnable,也就是我们的
//FutureTask。
mTasks.offer(new Runnable() {
public void run() {
try {
//run方法会在线程池中进行
r.run();
} finally {
//进行下面的scheduleNext()方法
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
这个exec是一个继承了Executor的SerialExecutor类。
在它的execute方法里它主要干了这几件事情:
①创建一个队列,装runnable,也就是我们之前提交过来的FutureTask。
②用THREAD_POOL_EXECUTOR来处理我们队列里的对象。
这里注意是run方法执行完毕后才执行scheduleNext方法,因此我们的任务在这里是线性执行的。
那么下一个问题来了,THREAD_POOL_EXECUTOR又是什么呢?顾名思义,这是一个线程池,设置了一些线程池的参数创建出来的,用来执行我们的FutureTask对象。
这里就执行了我们的FutureTask对象,还记得我们FutureTask对象里封装了什么吗?对,是一个Callable类型的WorkerRunnable,我们来看源码
//我们来看主要的
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
//这里的c被赋予了我们传进来的WorkerRunnable对象
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//调用了WorkerRunnable的call方法
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);
}
}
很明显,当FutureTask被执行的时候,会调用其run方法,run方法中调用了Callable也就是我们的WorkerRunnable对象的call方法。
简洁明了,我们去看这个call方法。
mWorker = new WorkerRunnable() {
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;
}
};
call方法里做了什么呢,
①设置了线程池的优先级
②调用了第二个回调方法,doInBackground方法,这也就是为什么这个方法是在后台进行的
③调用postResault方法,我们点进去看一下
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//getHandler就会获得我们之前赋值为主线程handler的Handler对象
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
做的事情很简单,就是通过主线程的handler把结果发送出去。
到此,AsycnTask的流程就结束了,还有一些方法没有涉及到,但是都很简单,大家可以去看源码,源码真的很有必要去看,这样能更加深刻的记住它的工作流程。谢谢~