多线程的应用在Android开发中是非常常见的,常用方法主要有:
1.继承Thread类
2.实现Runnable接口
3.Handler
4.AsyncTask
5.HandlerThread
常用的异步框架:
1.RxJava/RxAndroid
2.EventBus
Google提官方提供的类-AsyncTask, 如何使用这个类,内部原理是如何实现的?下面就来拆解看看AsyncTask的源码。
AsyncTask是一个抽象类,核心方法如下:
方法的执行顺序:
AsyncTask的实现有三个泛型参数AsyncTask<Params, Progress, Result>:
Params:execute传入的参数类型;
Progress:执行过程中onProgressUpdate回调方法的参数,再doInbackgroud异步执行时,可以调用publishProgress方法传递任务进度等信息,比如下载时的进度百分比等;
Result:异步任务结束时,如果有返回结果需要回到主线程中处理,则将数据return给onPostExecute方法。
AsyncTask使用:
1.创建 AsyncTask 子类 & 根据需求实现核心方法;
2.创建 AsyncTask子类的实例对象(即 任务实例);
3.手动调用execute(()从而执行异步线程任务。
AsyncTask核心设计:
线程池 + Handler,通过线程池对异步任务线程管理,重复利用减少线程的创建开销,同时通过handler回到主线程,这样可以实现主线程与子线程的交互。
AsyncTask的线程池设计:
AsyncTask内部有一个线程池和一个任务队列;
任务队列(SerialExecutor):任务调度,执行多个线程任务时按顺序调度;
执行线程池(THREAD_POOL_EXECUTOR):真正执行具体任务的线程。
AsyncTask源码:
execute(Params …)
executeOnExecutor(sDefaultExecutor, params)
sDefaultExecutor线程池后面描述。
->executeOnExecutor
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);
先判断当前asynctask的执行状态,如果已经执行则再次调用execute方法会报错!
mWorker是一个Callable对象;
mFuture是一个FutureTask对象,线程池的任务执行对象。
以上时AsyncTask的执行方法内容,代码很少,那mFuture以及mWorker是什么时候创建的?继续看AsyncTask的构造函数:
AsyncTask()
AsyncTask(@Nullable Handler handler)
AsyncTask(@Nullable Looper callbackLooper)
一般如果我们不传入Looper或Handler,则使用MainHandler,如果传入了非MainLooper的Looper或handler,则创建一个Handler使用当前传入的looper。也就是说默认返回到主线程,如果特别指定其它线程的handler或looper则返回到指定线程。
接着实例化mWorker对象:
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;
}
};
接着实例化mFutureTask对象(不了解可以查阅Java并发编程相关api):
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);
}
}
};
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
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;
}
那么继续看执行方法,执行方法最后对mWorker设置了参数,并通过线程池提交了任务:
mWorker.mParams = params;
exec.execute(mFuture);
则执行mFuture的run方法时会调用mWorker的call方法,并返回结果。
mFutureTast路线:run->mWorker->set(result)-> finishCompletion()->done->AsyncTask。
上面关于AsyncTask执行路径的相关源码就差不多这么多了,那AsyncTask的核心机制线程池的源码接着来拆解一下:
AsyncTask里面定义了一个线程池以及一个任务队列,变量名如下:
THREAD_POOL_EXECUTOR、SERIAL_EXECUTOR
THREAD_POOL_EXECUTOR的定义如下:
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
其实就是实例化了一个ThreadPoolExecutor,各参数如下:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
AsyncTask对线程池的大小设置以及等待队列的大小在自定义线程池时可以参考这里的写法,为啥不用Java提供的ExecutorService的几种方式来创建?线程池的内部原理实现后面会做单独文章介绍。
SERIAL_EXECUTOR的定义如下:
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对象共享,可以看出当asyncTask执行execute时,会将mFutureTask添加到队列中,当一个任务执行后会从队列中再获取一个任务执行,执行时使用线程池来执行任务,直到所有的任务都被执行,poll返回null则停止。
AsyncTask的状态:
PENDIG:等待
RUNNING:执行中
FINISHED:结束
AsyncTask的优点:
方便:可以在主线程和子线程进行通信,在子线程中处理耗时任务,将结果返回主线程进行UI等操作不需要开发者另行切换(Thread+Handler的方式);
节约:内部采用缓存线程池+复用线程池的组合方式,避免了频繁创建、销毁线程,节省系统对线程处理的开销,节约资源。
AsyncTask的适用场景:
AsyncTask适合用于有相同参数和返回值的一类异步任务,比如网络请求单个接口请求可以定义一个AsyncTask对象,处理该接口的异步处理以及将结果返回主线程用于UI的更新。但如果只是需要异步处理耗时任务,比如数据库保存操作,文件保存等,或没有固定的参数与返回结果类型,为每个异步任务都定义一个AsyncTask会使得代码很乱,且不好控制。且AsyncTask单次执行后不可复用,且没有超时机制,除非手动取消,否则会内存泄漏。
AsyncTask使用注意点:
AsyncTask不与任何组件绑定生命周期,在activity或fragment结束时将内部启动的asynctask手动cancel处理,避免内存泄漏;
AsyncTask应被声明为Activity的静态内部类,非静态类会导致activity等对象内存泄漏。