原理:
使用 ThreadPool + Handler 实现:在子线程进行耗时操作,UI线程实现UI更新,防止阻塞UI线程。即:子线程向UI线程切换。
执行步骤
- 创建AsyncTask实例(必须在UI线程) new AsyncTask(Params… params)
- asynctask.excute(),使用线程池执行异步任务(必须在UI线程)
WorkerRunnable extents Callable<T>;
public AsyncTask(@Nullable Looper callbackLooper) {
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;
}
};
mFuture = new FutureTask<T>(mWorker);
}
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
// 状态检测
*****
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture); =》 sDefaultExecutor.excute(mFuture)
return this;
}
- onPreExecute():运行在UI线程,所以可以做一些任务开始前的UI更新
- doInBackground(params):执行在Callable(子线程)的call()中,做一些耗时操作,并且可以得到耗时结果-result,然后调用postResult(result)。将result封装成message,通过handler将message传送到UI线程中的handleMessage(message)。
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
// 在doInBackgound中手动调用publishProgress,会发送该事件
result.mTask.onProgressUpdate(result.mData);
break;
}
}
- onProgressUpdate(result):执行在UI线程中,可以显示进度
- onPostExecute(result):执行在UI线程中,得到dongInBackground的耗时结果,刷新UI。
注意事项
- 一个AsyncTask实例只能执行一次excute,因为在excute中会有状态检测。
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("***");
case FINISHED:
throw new IllegalStateException("***");
}
}
mStatus = Status.RUNNING;
- AsyncTask的创建于执行必须在UI线程。
- AsyncTask使用了线程池,那么线程池的核心线程数和最大线程数是如何计算的?
// 获取当前设备德 CPU (现在普遍是6核)
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// 核心线程数范围 [2,4]
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
// 最大线程数公式:2*CPU+1
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
- Callable与Runnale的区别
Callable只是一个可以返回数据的接口,只有在将它与FutureTask组合后(new FutureTask<Params,Result>(Callable callable)),才能实现子线程返回数据的功能。
FutureTask implements RunnableFuture; RunnableFuture extents Runnable, Future。
普通的Runnable是不带执行结果返回的。但是使用FutureTask + Callable便可以实现带有返回结果的任务。
扩展
- 何时出发线程池的拒绝策略?
提交的任务数大于 CORE_POOL_SIZE 时,会优先将任务放到 队列缓冲区(QueueCapacity) 中,当把 队列缓冲区 填满后,再去判断当前运行的任务数是否大于 MAXIMUM_POOL_SIZE ,若小于则创建新的线程处理任务,若是大于时则会触发拒绝策略。总结:T(触发条件) = Runnable > (MAXIMUM_POOL_SIZE + QueueCapacity).
- 线程池的拒绝策略有几种?
CallerRunsPolicy(调用者策略):
AbortPolicy(中止策略):
DiscardPolicy(丢弃策略):
- MessageQueue(链表)是实时插入Message还是延时插入Message?
bool enqueueMessage(Message msg, long when){
....
// 遍历链表,按照执行时间顺序插入链表
for(;;){
pre = p;
p = p.next;
if(p == null && when < p.when){
break;
}
....
}
// 插入链表
msg.next = p;
pre.next = msg;
...
}
- Message的类型有哪些?
- 异步消息
// 通过Handler的构造创建异步消息,通过这种方式,则内部所有的消息都是异步的
public void Handler(Callback callback, bool async){
...
mCallback = callback;
mAsynchronous = async;
}
// 通过主动调Message.setAsynchronous(true),将消息设为异步
Message msg = Message.obtain();
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
- 同步消息
// 凡是没有设置mAsynchronous = true的消息都是同步消息
Message msg = new Message();
msg = Message.obtain();
handler.sendMessage(msg);
handler.post(new runnable());
- 同步屏障:阻塞MessageQueue中的同步消息,只有正在屏障标识移除后,MessageQueue才会被唤醒。对异步消息没有作用
// 使用场景:在刷新UI时,会发送一个同步屏障,阻塞MessageQueue,保证UI线程刷新时阻塞同步消息。
ViewRootImpl中
void scheduleTraversals(){
···
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
···
}
void unscheduleTraversals() {
···
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
···
}
// 发送一个消息屏障
public int postSyncBarrier(long when){return token;}
// 移除一个消息屏障
public void removeSyncBarrier(int token) {}
- Handler机制的阻塞与唤醒:(插入消息时唤醒,读取消息时阻塞)。结合生产者/消费者模式理解。
阻塞调用栈:Looper.loop() -> MessageQueue.next() -> nativePollOnce() -> MessgaeQueue.cpp->pollOnce() -> Looper.cpp->pollOnce() - > Lopper.cpp.pollInner() -> epoll_wait() -> read() 从队列中读取消息,没有消息时,阻塞队列。
唤醒调用栈:Handler.sendMessageAtTime() -> MessageQueue.enqueueMessage() -> nativeWake() -> MessageQueue.cpp->wake() -> Looper.cpp.wake() -> write() -> 向队列中写入消息,唤醒队列