前言
Android
中,线程是操作系统调度的最小单位。线程分为主线程和子线程。主线程用来处理界面的交互,而耗时操作(网络请求,复杂的数据库查询)必须在子线程中来完成。通过Handler
消息机制完成主线程和子线程之间的通信。
每个任务都需要一个线程去执行,但是不可能每个任务的执行都是伴随着线程的销毁和重新创建,十分的耗费性能。所以用线程池去缓存一定数目的线程,由线程池来管理执行任务的线程,避免了频繁的创建和销毁。Android
中提供了AsyncTask
类,内部就是由线程池和Handler
实现的。各位大佬,这篇看了包会!
(一) AsyncTask使用
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask
是一个抽象类,如果使用的话需要自定义一个类继承它。它提供了4个核心的方法。这里用一个示例来做介绍。
一、使用核心方法介绍
1.1 自定义类继承AsyncTask
//静态属性声明处,此处MyAsyncTask为静态类
companion object {
class MyAsyncTask(private var taskName: String) : AsyncTask<String, Int, String>() {
//核心方法1
override fun onPreExecute() {
super.onPreExecute()
Log.d("MyAsyncTask", taskName + " OnPreExecute")
}
//核心方法2
override fun doInBackground(vararg params: String?): String {
try {
Thread.sleep(5000)
} catch (e: Exception) {
e.printStackTrace()
}
//辅助方法
publishProgress(50)
Log.d("MyAsyncTask", taskName + " doInBackground")
return taskName
}
//核心方法3
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
Log.d("MyAsyncTask",
"I am Task" + result + " executed at " + SimpleDateFormat("yyyy-mm-dd HH:mm:ss").format(Date()))
}
//核心方法4
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
Log.d("MyAsyncTask", taskName + " onProgressUpdate")
}
}
}
声明好了静态内部类MyAsyncTask
后,执行异步任务。
execute!!.setOnClickListener({
MyAsyncTask("AsyncTask1").execute()
})
介绍一下上面点击按钮执行任务操作,在项目中是没有写findViewById
去加载Button
,通过Kotlin Android Extensions
拓展插件直接可以使用xml
布局里定义的控件,有兴趣的同学可以去Kotlin
官网了解一波。
下面是MyAsyncTask
执行的结果。
根据图示介绍一下AsyncTask
的核心方法
1-2、onPreExecute
在主线中执行,用于异步任务执行之前做一些准备工作,比如初始化加载进度条之类。
1-3、doInBackground(vararg params: String?)
在子线程中执行,真正执行异步任务的方法,参数是异步任务输入参数。在此方法中可以通过publishProgress
方法来更新异步任务当前的进度,调用publishProgress
方法会同步调用onProgressUpdate
。
1-4. onProgressUpdate(vararg values: Int?)
在主线程中执行,此方法用来实时显示当前任务的执行进度。作用是可以在此处更新UI上的进度条进度或者做其他操作。
1-5. onPostExecute(result: String?)
在主线程中执行,异步任务执行结束之后会执行此方法。参数是doInBackground
的返回值。
AsyncTask
的核心方法就是上面所述,执行任务是在子线程中,而任务准备工作,进度更新等全是在主线程中完成。AsyncTask
实例的创建也是要在主线程中去执行。
二、源码解析
根据代码执行顺序,我们首先看下AsyncTask
的 execute
方法。
//主线程中执行
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... 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后台执行任务所需参数
mWorker.mParams = params;
//线程池执行线程任务,mFuture封装了开启线程的mWorker
exec.execute(mFuture);
return this;
}
我们看下上面executeOnExecutor
方法中几个重要的属性。exec →SerialExecutor
,mWorker → WorkerRunnable
和mFuture → FutureTask
。
WorkerRunnable:任务执行返回的结果
这里mWorker
对象的创建是在AsyncTask
的构造函数中,本质是开启一个工作线程,call
方法将任务执行完的结果返回。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//标记任务已经被调用过
mTaskInvoked.set(true);
Result result = null;
try {
//将线程的优先级设置为后台,为了执行完任务方便回收
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//后台执行任务
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
//有异常将此任务取消
mCancelled.set(true);
throw tr;
} finally {
//任务结束
postResult(result);
}
return result;
}
};
FutureTask:线程任务(理解为要执行的任务)
这里将mWorker
任务的参数封装成FutureTask
对象,按照中式英语理解的话就是将来要执行的任务。本质是一个Runnable
对象。
mFuture = new FutureTask<Result>(mWorker) {
@Override
//任务完成后的回调
protected void done() {
try {
//调用下方postResult方法
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) {
//调用下方postResult
postResultIfNotInvoked(null);
}
}
};
当线程任务执行结束后,调用postResult(Result result)
通过InternalHandler
将线程池(子线程任务)切换到主线程中。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
getHandler
获取的就是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;
}
}
}
exec:线程池执行类
exec
是SerialExecutor
串行线程池对象,主要作用是将要执行的任务mFutureTask
通过此类被插入到任务队列mTasks
中,整合任务执行顺序作用。会不断从队列中取出任务来执行,如果当前任务执行完毕,mTasks
会查找队列顶端是否有mFutureTask
对象。有的话就执行。没有任务结束。
//源码中不是这样写的,这里为了方便理解
public static final Executor exec = new SerialExecutor();
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() {
//队列最上方的一个元素不为null即队列顶端有空闲的任务
if ((mActive = mTasks.poll()) != null) {
//真正执行任务的线程池:THREAD_POOL_EXECUTOR核心类ThreadPoolExecutor对象
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
其实真正执行任务的线程池是:ThreadPoolExecutor
核心类的对象THREAD_POOL_EXECUTOR
THREAD_POOL_EXECUTOR.execute(mActive);
目前 AsyncTask
在Android3.0
以上版本的执行顺序是串行的,有个多个任务添加到mTasks
队列中,执行的顺序是从队列顶端往下依次执行。如果想实现多线程的并行任务。只要将
MyAsyncTask("AsyncTask1").execute()
替换为即可:
MyAsyncTask("AsyncTask1").executeOnExecutor()
总结
Android
中多线程任务虽然没有Java
中使用频率高,但是是一个比较重要的知识点。AsyncTask
可以在多线程任务中发挥好的作用,比如项目中的在线更新等。线程池也是特别重要的知识点,几乎是面试必问,接下来将会介绍线程池的工作原理等相关知识。