除了我们常用的Handler进行耗时操作,最后更新到UI线程中,虽然Handler比较简单,但是如果有多个任务同时执行代码就会增多。所以我们可以使用一下AsynTask,它使得异步任务实现起来更加简单。
首先我们介绍一下如何使用:
我在这里实现了一个进度条的加载
//首先需要继承AsynTask这个抽象类,并指定三个参数类型
class MyTask extends AsyncTask<String,Integer,String>{
//执行线程任务前的操作
@Override
protected void onPreExecute() {
super.onPreExecute();
textView.setText("加载中");
}
//耗时操作
@Override
protected String doInBackground(String... strings) {
try {
int count=0;
int length=1;
while (count<99){
count+=length;
//调用该方法显示进度,之后将执行onProgressUpdate
publishProgress(count);
Thread.sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressBar.setProgress(values[0]);
textView.setText("loading..."+values[0]);
}
//执行完毕返回结果
@Override
protected void onPostExecute(String s) {
textView.setText("加载完毕");
}
//取消任务
@Override
protected void onCancelled() {
textView.setText("已取消");
progressBar.setProgress(0);
}
}
然后调用execute()时必须在主线程中调用:
myTask=new MyTask();
接下来我们看一看AsynTask源码吧!
public abstract class AsyncTask<Params, Progress, Result> {
...
}
三个参数:
Params:参数类型,一般就是我们需要输入的参数类型
Progress:任务执行进度的参数
Result:返回结果的类型
如果不需要其中的某个参数可将其设为void类型。
我们一般要实现的方法(这几个方法在源码中都是空实现):
onPreExecute():任务执行前的准备工作,在主线程执行,则可以进行UI操作。
doInBackground(Params...params):准备好之后开始进行的耗时任务操作,在线程池中执行,在执行中通过调用publishProgress(Progress...values)来进行更新进度。
onProgressUpdate(Progress...values):在主线程中执行,当调用publishProgress方法时会将进度更新到UI组件中。
onPostExecute(Result result):后台任务执行完毕,得到doInBackground中的结果,在主线程执行,能够进行UI操作。
onCancelled:在主线程执行,当需要取消异步任务时调用,需要在ondoInBackground去判断状态
AsynTask在3.0之前的版本与3.0之后的版本有较大改动,我们需要知道主要改动在哪。
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final it KEEP_ALIVE = 10;
……
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
Android3.0之前的版本中核心线程数为5,最大线程数为128,非核心线程空闲等待新任务最长时间为10s。
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int BACKUP_POOL_SIZE = 5;
private static final int KEEP_ALIVE_SECONDS = 3;
我现在的版本中只有一个核心线程,最大线程数为20,备份池大小为5,非核心线程空闲等待时间为3s。
我么看一下AsynTask的构造函数:
public AsyncTask() {
this((Looper) null);
}
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);
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);
}
}
};
}
关键代码1:WorkerRunnable实现了Callable接口,并实现了call方法,在call中调用了doInBackground(mParams)处理任务得到结果,最终调用postResult提交结果。
关键代码2:FutureTask是一个可管理的异步任务,实现了Runnable和Future这两个接口。因此可以包装Runnable和Callable,并提供给Executor执行。在这里WorkerRunnable作为参数传递给FutureTask,这两个变量会保存到内存中,之后会用到。
在执行时我们在主线程调用execute()方法:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
//这里传入sDefaultExecutor跟params
return 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();
mWorker.mParams = params;
//将mFuture传入
exec.execute(mFuture);
return this;
}
在实际执行中先将状态变成RUNNING,之后调用onProExecute(),onProExecute中到底做了什么?,并将Params赋值给WorkerRunnable中。之前我们知道WorkerRunnable又传递给FutureTask,因此参数被封装到FutureTask中。之后会调用exec.execute(mFuture),exec又是什么?
首先我们看一下onProExecute中的源码:
@MainThread
protected void onPreExecute() {
}
是一个空实现的方法,这就需要我们重写该方法,并实现准备的操作,运行在主线程,可进行UI操作。
我们在exectueOnExecutor()中传入了sDefaultExecutor和params。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//sDefaultExecutor实际就是SerialExecutor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
//关键代码1
mTasks.offer(new Runnable() {
public void run() {
try {
//关键代码2
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SerialExector是一个串行的线程池,当调用exectue方法时会将FutureTask加入到mTask中,当前任务执行完或者当前没有任务时会执行scheduleNext方法,会将任务交给THREAD_POOL_EXECUTOR来处理。
关键代码2处执行了FutureTask的run方法,最终会调用WorkerRunnable的call方法,call又会最终调用postResult。
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//c实际上就是WorkerRunnable
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就是调用传进来的WorkerRunnable的call方法,我们在AsynTask的构造函数中可以看到call中最后有调用的是postResult。
private final Handler mHandler;
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private Handler getHandler() {
return mHandler;
}
似乎看到了点Handler的影子,没错,AsyTask内部通过创建消息,并将结果赋值给Message,通过getHandler得到Handler对象,并通过sendToTarget发送消息。
其实在上面构造函数代码中就已经初始化了Handler:
//构造函数中获取mHandler
//如果callbackLooper为null或者==Looper.getMainLooper就直接获取主线程Handler,否则会重新创建
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//获取主线程的Handler
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
//关键代码
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
一般情况下都是调用的getMainHandler(),内部实际创建的是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;
}
}
}
在AsynTask中定义了一个私有内部类用来处理消息。接收到MESSAGE_POST_RESULT之后会调用AsynTask的finish()方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果AsynTask任务被取消了则会调用onCancelled方法,否则就调用onPostExecute。最后我们就是同个onPostExectue获取结果并进行更新UI操作。
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();
}
}
在AsynTask中内部有两个线程池,一个就是之前我们说到的SerialExecutor,一般默认情况下都会使用这个线程池,使用时同一时间只能执行一个任务,一个任务执行完之后才会执行另一个任务。它使用了ArrayQueue队列,如果我们一次性启动很多任务,就会调用队列中的offer方法将任务加入到队列的尾部,并判断mActive是否为null,第一次运行时为null会执行scheduleNext,在finally中也调用了sceduleNext,这样就保证了每次执行完一个任务下一个任务才执行,该线程主要是将任务进行排列。
我么看一下scheduleNext源码:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
实际上使用的THREAD_POOL_EXECUTOR
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int BACKUP_POOL_SIZE = 5;
private static final int KEEP_ALIVE_SECONDS = 3;
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
//赋值
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
//线程工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
最后赋值操作将threadPoolExecutor赋值给THREAD_POOL_EXECUTOR。其实该线程池才是任务的真正执行者。
1.生命周期
AsynTask不与任何组件生命周期进行绑定,在使用的对象中销毁时最好要调cancel()方法。
2.内存泄漏
一般我们声明都是一个内部类形式,内部类默认持有外部类的引用,当外部类使用完毕但是AsynTask中耗时任务还没执行完毕导致外部类无法回收,进而造成内存泄露
3.结果丢失
在进行屏幕旋转或者Activity进程被杀死之后会重新创建Activity,之前运行的AsynTask会持有之前Activity的引用,但该引用已经无效,导致onPostExectuor无法更新结果。
4.AsynTask并行执行
我们可以通过AsynTask的executeOnExecutor传入我们自定义的线程池,也可以传入AsynTask自身的THREAD_POOL_EXECTOR线程池。
首先我们要创建AsynTask对象,在创建时会先去获取Handler对象,之后回去创建一个WorkerRunnable对象,之后会将WorkerRunnable当作参数传入到FutureTask中,创建好AsynTask对象之后就可以调用execute方法,实际上调用的是executeOnExecutor(sDefaultExectuor,mParams),内部是一个SerialExecutor对象,SerialExecutor是一个串行线程池,所有任务都在这里排队执行,之后先去执行onPreExecute(),该方法需要我们自己实现,之后线程池开始执行,并将FutureTask传入,并执行SerialExecutor中的execute()(内部调用了FutureTask的run方法,实际就是调用了我们之前创建的WorkerRunnable对象的call方法,在call内部执行了doInBackground方法,我们只需要将耗时操作写在ondoInBackground中即可),从这里就从主线程切换到了子线程,首先判断任务中有没有正在活动的任务,如果没有则会调用scheduleNext获取一个任务,当任务执行完毕之后在finally中再去调用scheduleNext,保证了该任务执行完毕之后再调用下一个任务,在scheduleNext内部通过THREAD_POOL_EXECUTOR进行任务执行。而在内部还有一个静态Handler对象,他的作用就是将子线程切换到主线程,为了能够进行更新UI操作,只要保证AsynTask在主线程创建即可。我们在doInBackground中通过publishProgress()创建消息,并通过Handler将消息发送出去,AsynTask中的InternalHandler中handleMessage调用onProgressUpdate()方法进行进度更新。当任务执行完毕doInBackground会返回结果到postResult中,再去发送消息给Handler,handler中会调用finish(),这样任务就执行完毕。