这篇文章是基于WorkManager原理的一个拓展,如果不熟悉WorkManager原理的可以先看一下下面这篇文章:
我不知道大家在看WorkManager源码的时候有没有这样一个疑惑,就是一个线程池里面执行一个Runnable,然后这个Runnable里面好像又有一个线程池执行Runnable,这样不就是线程里面套线程了吗?很显然,Google是不可能犯这样低级的错误的,那么我们就一起来看下这个线程池倒计时怎么设计的吧!
我们先来看一下下面的代码,不理解的翻一下上一篇文章:
- @Override
- public @NonNull Operation enqueue() {
- // Only enqueue if not already enqueued.
- if (!mEnqueued) {
- EnqueueRunnable runnable = new EnqueueRunnable(this);
- mWorkManagerImpl.getWorkTaskExecutor()
- .executeOnBackgroundThread(runnable);//1
- mOperation = runnable.getOperation();
- } else {
- Logger.get().warning(TAG,
- String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
- }
- return mOperation;
- }
这是WorkContinuationImpl里面的enqueue方法,我们看源码知道EnqueueRunnable继承了Runnable接口,注释1处的意思是将这个Runnable放到线程池里面去执行。但是,真的是这样吗?
EnqueueRunnable里run方法又会调用:
- @VisibleForTesting
- public void scheduleWorkInBackground() {
- WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
- Schedulers.schedule(
- workManager.getConfiguration(),
- workManager.getWorkDatabase(),
- workManager.getSchedulers());
- }
最后会走到GreedyScheduler里面:
mWorkManagerImpl.startWork(workSpec.id);
回到WorkManagerImpl:
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public void startWork(@NonNull String workSpecId) {
- startWork(workSpecId, null);
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public void startWork(
- @NonNull String workSpecId,
- @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
- mWorkTaskExecutor
- .executeOnBackgroundThread(
- new StartWorkRunnable(this, workSpecId, runtimeExtras));
- }
问题来了,到目前为止一路走下来怎么出现了两次executeOnBackgroudThread?
我们先来看看这个mWorkTaskExecutor究竟是个什么东西?
注:mWorkManagerImpl.getWorkTaskExecutor()返回的也是mWorkManagerImpl。
- private void internalInit(@NonNull Context context,
- @NonNull Configuration configuration,
- @NonNull TaskExecutor workTaskExecutor,
- @NonNull WorkDatabase workDatabase,
- @NonNull List
schedulers, - @NonNull Processor processor) {
- ···
-
- mWorkTaskExecutor = workTaskExecutor;
-
- ···
- }
在WorkManagerImpl里面找到初始化的地方了,接着看调用这个方法是在什么地方。
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public WorkManagerImpl(
- @NonNull Context context,
- @NonNull Configuration configuration,
- @NonNull TaskExecutor workTaskExecutor,
- @NonNull WorkDatabase database) {
- Context applicationContext = context.getApplicationContext();
- Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
- List
schedulers = - createSchedulers(applicationContext, configuration, workTaskExecutor);
- Processor processor = new Processor(
- context,
- configuration,
- workTaskExecutor,
- database,
- schedulers);
- internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
- }
接着往上找:
- public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
- synchronized (sLock) {
- if (sDelegatedInstance != null && sDefaultInstance != null) {
- throw new IllegalStateException("WorkManager is already initialized. Did you "
- + "try to initialize it manually without disabling "
- + "WorkManagerInitializer? See "
- + "WorkManager#initialize(Context, Configuration) or the class level "
- + "Javadoc for more information.");
- }
-
- if (sDelegatedInstance == null) {
- context = context.getApplicationContext();
- if (sDefaultInstance == null) {
- sDefaultInstance = new WorkManagerImpl(
- context,
- configuration,
- new WorkManagerTaskExecutor(
- configuration.getTaskExecutor()));//1
- }
- sDelegatedInstance = sDefaultInstance;
- }
- }
- }
我们找到了WorkManagerImpl初始化的时候,注意注释1处,原来这是一个WorkManagerTaskExecutor!前面嵌套调用的方法:
- @Override
- public void executeOnBackgroundThread(Runnable runnable) {
- mBackgroundExecutor.execute(runnable);
- }
里面又调用了一层,接着看:
- private final SerialExecutor mBackgroundExecutor;
-
- public WorkManagerTaskExecutor(@NonNull Executor backgroundExecutor) {
- // Wrap it with a serial executor so we have ordering guarantees on commands
- // being executed.
- mBackgroundExecutor = new SerialExecutor(backgroundExecutor);
- }
原来调用的是SerialExcutor的execute方法!在回到注释1处的WorkManagerTaskExcutor的初始化:
new WorkManagerTaskExecutor(configuration.getTaskExecutor())
可见:SerialExecutor里面传入的是configuration.getTaskExecutor()。
接着看:
- @NonNull
- public Executor getTaskExecutor() {
- return mTaskExecutor;
- }
-
- mTaskExecutor = createDefaultExecutor(true /* isTaskExecutor */);
-
- private @NonNull Executor createDefaultExecutor(boolean isTaskExecutor) {
- return Executors.newFixedThreadPool(
- // This value is the same as the core pool size for AsyncTask#THREAD_POOL_EXECUTOR.
- Math.max(2,
- Math.min(Runtime.getRuntime().availableProcessors() - 1,
- 4)),
- createDefaultThreadFactory(isTaskExecutor));
- }
可见,线程池原来在这里创建的!
- public class SerialExecutor implements Executor {
- private final ArrayDeque
mTasks; - private final Executor mExecutor;
- private final Object mLock;
- private volatile Runnable mActive;
-
- public SerialExecutor(@NonNull Executor executor) {
- mExecutor = executor;
- mTasks = new ArrayDeque<>();
- mLock = new Object();
- }
-
- @Override
- public void execute(@NonNull Runnable command) {
- synchronized (mLock) {
- mTasks.add(new Task(this, command));
- if (mActive == null) {
- scheduleNext();
- }
- }
- }
-
- // Synthetic access
- void scheduleNext() {
- synchronized (mLock) {
- if ((mActive = mTasks.poll()) != null) {
- mExecutor.execute(mActive);
- }
- }
- }
-
- /**
- * @return {@code true} if there are tasks to execute in the queue.
- */
- public boolean hasPendingTasks() {
- synchronized (mLock) {
- return !mTasks.isEmpty();
- }
- }
-
- @NonNull
- @VisibleForTesting
- public Executor getDelegatedExecutor() {
- return mExecutor;
- }
-
- /**
- * A {@link Runnable} which tells the {@link SerialExecutor} to schedule the next command
- * after completion.
- */
- static class Task implements Runnable {
- final SerialExecutor mSerialExecutor;
- final Runnable mRunnable;
-
- Task(@NonNull SerialExecutor serialExecutor, @NonNull Runnable runnable) {
- mSerialExecutor = serialExecutor;
- mRunnable = runnable;
- }
-
- @Override
- public void run() {
- try {
- mRunnable.run();
- } finally {
- mSerialExecutor.scheduleNext();
- }
- }
- }
- }
阶段性总结一下:mWorkTaskExecutor.executeOnBackgroundThread中的mWorkTaskExecutor是WorkManagerTaskExecutor,executeOnBackgroundThread调用链条:
executeOnBackgroundThread()->SerialExecutor的execute(),我们来重点看下这个方法:
- @Override
- public void execute(@NonNull Runnable command) {
- synchronized (mLock) {
- mTasks.add(new Task(this, command));
- if (mActive == null) {
- scheduleNext();
- }
- }
- }
-
仅仅是把任务加入了Task,然后调用Runnable自身的run方法,跟线程池没有半毛钱关系:
- static class Task implements Runnable {
- final SerialExecutor mSerialExecutor;
- final Runnable mRunnable;
-
- Task(@NonNull SerialExecutor serialExecutor, @NonNull Runnable runnable) {
- mSerialExecutor = serialExecutor;
- mRunnable = runnable;
- }
-
- @Override
- public void run() {
- try {
- mRunnable.run();
- } finally {
- mSerialExecutor.scheduleNext();
- }
- }
- }
所有嵌套任务都加入Task后,调用 scheduleNext():
- // Synthetic access
- void scheduleNext() {
- synchronized (mLock) {
- if ((mActive = mTasks.poll()) != null) {
- mExecutor.execute(mActive);
- }
- }
- }
这个才是最终线程池执行的任务!也就是最里面的一层。这下大家清楚了吧,这个设计确实非常巧妙!