零零碎碎的东西老是记不长久,仅仅学习别人的文章也只是他人咀嚼后留下的残渣。无心中发现了这个每日一道面试题 ,想了想若是只是简单地去思考,那么不只会收效甚微,甚至难一点的题目本身可能都懒得去想,坚持不下来。因此不如把每一次的思考、理解以及别人的看法记录下来。不只加深本身的理解,更要激励本身坚持下去。java
前几天的那个面试弄得我一脸懵逼,如今有点空闲时间,仔细的探讨一下当时的问题。印象最深的就是关于 HandlerThread 与 AsyncTask 的问题。首先列一下探讨过程当中想到的问题以及面试时问的问题。android
先简单讲讲这两个的源码吧,都挺简单的,并无那么复杂。如下源码来自于 Android-28git
HandlerThread 其实是 Thread+Looper+Handler 的一个简单封装。github
HandlerThread 类继承 Thread 类,因此须要 start() 方法来开启线程。面试
HandlerThread handlerThread = new HandlerThread("workThread");
handlerThread.start();
Handler threadHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//根据不一样的 message 类型处理不一样的耗时任务
}
};
复制代码
这是简单的使用方法。建立 Handler 时须要传入 HandlerThread 中的 Looper 对象,这样 handlerMessage 方法就会在子线程中处理耗时任务。须要耗时任务能够经过 threadHandler.sendMessage() 发送消息,而后下 handlerMessage 方法中进行处理。网络
源码中最重要的就是一个重写的 run 方法。并发
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();//唤醒线程能够获取 Looper 对象了
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
复制代码
也很简单,在咱们执行 handlerThread.start() 开启一个线程后,就会执行此方法。经过 Looper.prepare() 在此线程中建立一个 Looper 对象,而后通知其余线程能够获取 Looper 对象,设置线程优先级。onLooperPrepared() 是一个空的方法,咱们能够重写此方法进行一些 Looper 开启 loop 循环以前的准备。less
一切都准备好以后,就是经过 Handler 发送消息,而后在 handlerMessage() 中进行耗时操做。简单说明一下,Handler 类中的 handlerMessage() 方法在哪一个线程中执行,是由 Handler 中的 Looper 对象所在的线程决定的,这是由于在 loop 循环中经过 msg.target.dispatchMessage()--->handleMessage() 间接地调用了 handlerMessage 方法,而 Looper.loop 是在子线程中执行的。具体可看 android 的消息机制详解---每日一道面试题(第 9 期)---谈谈 Handler 机制和原理 异步
AsyncTask 则是线程池与 Handler 的封装ide
一些基本使用就不在详细说明了,主要来看看源码。首先是构造方法,有三个。无参、Handler、Looper,前两个都会调用第三个构造方法。
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(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<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);
}
}
};
}
复制代码
有点长,但其实就三步操做。
一般使用时经过 execute 方法开启任务,看看源码中干了什么。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
exec.execute(mFuture);
return this;
}
复制代码
execute 中执行的是 executeOnExecutor 方法,并传入 sDefaultExecutor 与耗时任务须要的参数。首先是检查状态,mStatus 是一个枚举变量,有 PENDING、RUNNING、FINSHED 三种状态,这三种状态都是惟一的,按 PENDING---RUNNING---FINISHED 顺序,初始化对象时是 PENDING,在 executeOnExecutor 中变为 RUNNING,在 finish 方法中更新为 FINSHED 状态。所以能够看出 executor 方法只能在一个对象中执行一次,屡次执行就会抛出异常。而后更新状态,调用 onPreExecute 方法,咱们能够种重写此方法作些进行耗时操做前的准备。传入参数,而后就是 exec.execute 提交任务,也就是构造函数中包装好的 FutureTask 对象。
这个 exec 是成员变量 sDefaultExecutor,是 AsyncTask 内部定义的静态类,实现了 Executor 接口。
private static class SerialExecutor implements Executor {
//双端队列,按照先进先出的原则储存 FutureTask 对象
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
//对传入的 mFuture 又进行了一次封装,以便于串行处理任务
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) {
//由此可看出 SerialExecutor 只负责任务的串行处理,真正的耗时任务操做是交给 THREAD_POOL_EXECUTOR 线程池进行调度
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
复制代码
这是一个静态类,也就是说全部的耗时任务都要通过此类进行串行处理。SerialExecutor 就是为了使耗时任务可以串行的被处理才存在的,真正处理耗时任务的则是 THREAD_POOL_EXECUTOR 线程池。
//得到没有睡眠的 CPU 数量
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
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);//cas 操做的 int 变量
public Thread newThread(Runnable r) {
//记录建立线程的数量
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final 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;
}
复制代码
以上就是建立了一个新的线程池,并赋值给静态变量 THREAD_POOL_EXECUTOR,这部分操做是在静态代码块中进行的,也就是说只会在被类加载的时候执行一次。全部的耗时任务都是在这仅有的一个线程池中执行任务。简单说下这个线程池中的参数。
剩下的就是线程池中调度 mFuture 执行耗时任务,执行其中的 mFuture 中 Callable 接口的 call 方法。其实就是上面说到的构造方法中初始化的 mWorker,其中对 call 方法进行了重写,在来了解下。
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;
}
};
复制代码
能够看出,在进行耗时操做后,不管是处理完,还有发生异常,都要 postResult() 方法进行收尾。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
复制代码
用 Handler 包装了一个信息,发送了出去。标记为 MESSAGE_POST_RESULT,意思就是耗时任务的结果。这个 Handler,就是构造函数中初始化的那个 Handler 对象,只不过在经过 getMainHandler 中是用自定义的静态内部 Handler 类进行了包装。
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
//AsyncTask 的静态内部类,方便传递结果数据与对应的 AsyncTask 对象
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;
}
}
}
复制代码
发送的消息就在这里被处理了。若是是 MESSAGE_POST_RESULT,就调用 finish 方法;若是是 MESSAGE_POST_PROGRESS,就是 onProgressUpdate(result.mData) 方法,也就是咱们能够用来重写更新进度的操做。MESSAGE_POST_PROGRESS 类消息只有在你调用 publishProgress 方法时才会被调用。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
复制代码
finish 方法中会根据 mCancel 的状态决定调用 onCancelled(result) 仍是 onPostExecute(result),也就是说这两个只会调用其中一个方法,这两个方法也是咱们在使用 AsyncTask 须要重写的方法。mCanael 咱们能够经过调用 cancel() 方法改变状态。
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
//在 mFuture 中中断线程
return mFuture.cancel(mayInterruptIfRunning);
}
复制代码
整个内部大体的流程就差不过了。将耗时任务封装进 FutureTask,SerialExecutor 对 FutureTask 在进行包装使耗时任务能够串行执行,最后由 THREAD_POOL_EXECUTOR 线程池进行真正的耗时任务调度处理。
HandlerThread 是在子线程的 loop 循环中进行的耗时操做,只有当前的耗时操做完成,才能获取下一个消息处理,因此是串行的。至于变为并行的,不能够。简单验证下
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.US);
Handler threadHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
HandlerThread handlerThread = new HandlerThread("workThread");
handlerThread.start();
threadHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("HandlerThread", "message" + msg.what + ":" + df.format(new Date()));
}
};
}
public void onLoginClick(View v){
int i = 0;
threadHandler.sendEmptyMessage(++i);
threadHandler.sendEmptyMessage(++i);
threadHandler.sendEmptyMessage(++i);
threadHandler.sendEmptyMessage(++i);
threadHandler.sendEmptyMessage(++i);
}
复制代码
自定义一个简单的 AsyncTask
static class MyAsyncTask extends AsyncTask<Void, Void, String>{
String name = "AsyncTask";
private MyAsyncTask(String name){
super();
this.name = name;
}
@Override
protected String doInBackground(Void... voids) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return name;
}
@Override
protected void onPostExecute(String string) {
super.onPostExecute(string);
Log.e("AsyncTask", string + ":" + df.format(new Date()));
}
}
复制代码
当咱们进行一连串的耗时任务时,就会串行处理
new MyAsyncTask("AsyncTask#1").execute();
new MyAsyncTask("AsyncTask#2").execute();
new MyAsyncTask("AsyncTask#3").execute();
new MyAsyncTask("AsyncTask#4").execute();
new MyAsyncTask("AsyncTask#5").execute();
new MyAsyncTask("AsyncTask#6").execute();
new MyAsyncTask("AsyncTask#7").execute();
new MyAsyncTask("AsyncTask#8").execute();
复制代码
new MyAsyncTask("AsyncTask#1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new MyAsyncTask("AsyncTask#2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new MyAsyncTask("AsyncTask#3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new MyAsyncTask("AsyncTask#4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new MyAsyncTask("AsyncTask#5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new MyAsyncTask("AsyncTask#6").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new MyAsyncTask("AsyncTask#7").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new MyAsyncTask("AsyncTask#8").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
复制代码
那么为何要默认串行处理任务,那样岂不是丢失了线程池的最大优点?
这是由于默认线程池其实所能容下的线程并很少,就拿 8 核的例子来看,最大线程数为 17,阻塞队列容量为 128,加起来所能容下的最大线程数为 17+128=145,在高并发的状况下很容易就会满,而且 THREAD_POOL_EXECUTOR 对象在整个应用程序中是惟一的。因此默认是串行处理,若是真的有高并发处理的状况,能够根据需求自定义线程池进行并发处理。
对于这个面试时抛出的问题,我简单说下个人理解。由于我实际开发经验少的可怜,因此说的可能有错误或者很片面,包括上面的几个问题的回答。如今网上的资料真的不敢随便相信,有的还自相矛盾,我突然明白面试时面试官问我日常都看谁的文章,知道哪些在 Android 方面比较专业的人士的用意了。
大量的耗时操做,若是任务之间没有什么关联的话,在我看来若是是串行处理的话都不怎么好,由于会阻塞后面的任务,而任务之间并不须要有个先后执行的顺序。因此在 AsyncTask 中并行处理比较好。而若是任务之间有关联,则需串行执行,此时就要看这些耗时任务的执行逻辑是否一致,若是不一致的话那就要自定义多个 AsyncTask,也非常麻烦。在我看来 AsyncTask 更多的是强调与 UI 线程的交互吧。
其实对于 Android 中的耗时任务处理,HandlerThread、IntentService、AsyncTask、ThreadPoolExecutor 这几个具体应用场景,有哪些差异,还真的说不出来个因此然来,还需努力。(总之就是菜(滑稽))