Located in the package: androidx. Arch. Core. The executor + + + + + + + + TaskExecutor ArchTaskExecutor + + + + DefaultTaskExecutor

Review the Jetpack found androidx. Arch. Core. The executor in the package there is a thread pool implementation class ArchTaskExecutor, internal realized the function of the switch to the main thread.

The way it is used is very smooth:

. / / switch to the main thread ArchTaskExecutor getInstance () executeOnMainThread (new Runnable {}); . / / the child thread execution ArchTaskExecutor getInstance () executeOnDiskIO (new Runnable {});Copy the code

The ArchTaskExecutor method is exposed:

  • executeOnDiskIO(Runnable runnable)
  • postToMainThread(Runnable runnable)
  • Executor getMainThreadExecutor()
  • Executor getIOThreadExecutor()
  • boolean isMainThread()
  • void setDelegate(TaskExecutor taskExecutor)

The ArchTaskExecutor class is constructed in this way:

ArchTaskExecutor inherits from the abstract TaskExecutor class. TaskExecutor is very simple, with only four methods, The logic in the executeOnMainThread(Runnable Runnable) method is that if the task is not on the main thread, postToMainThread(Runnable) will be called to switch the task to the main thread for execution.

public abstract class TaskExecutor {

    public abstract void executeOnDiskIO(@NonNull Runnable runnable);

    public abstract void postToMainThread(@NonNull Runnable runnable);

    public void executeOnMainThread(@NonNull Runnable runnable) {
        if (isMainThread()) {
            runnable.run();
        } else {
            postToMainThread(runnable);
        }
    }

    public abstract boolean isMainThread();
}
Copy the code

Looking at ArchTaskExecutor, ArchTaskExecutor implements a singleton that provides access to instances:

public class ArchTaskExecutor extends TaskExecutor { private static volatile ArchTaskExecutor sInstance; private TaskExecutor mDelegate; private TaskExecutor mDefaultTaskExecutor; . @NonNull public static ArchTaskExecutor getInstance() { if (sInstance ! = null) { return sInstance; } synchronized (ArchTaskExecutor.class) { if (sInstance == null) { sInstance = new ArchTaskExecutor(); } } return sInstance; } private ArchTaskExecutor() { mDefaultTaskExecutor = new DefaultTaskExecutor(); mDelegate = mDefaultTaskExecutor; }}Copy the code

Also found in the source code are mDelegate and mDefaultTaskExecutor, where mDelegate is the proxy class of ArchTaskExecutor implementation, responsible for the specific implementation of the function. MDefaultTaskExecutor is an implemented default thread pool proxy class. MDelegate is assigned to an instance of mDefaultTaskExecutor by default. External custom thread pools are also supported.

public class ArchTaskExecutor extends TaskExecutor { ... Public void setDelegate(@taskExecutor TaskExecutor) {mDelegate = TaskExecutor == null? mDefaultTaskExecutor : taskExecutor; }}Copy the code

Let’s look at the method implementation exposed in ArchTaskExecutor from the beginning:

public class ArchTaskExecutor extends TaskExecutor { ... @nonNULL Private static Final Executor sMainThreadExecutor = new Executor() {@override public void execute(Runnable command) { getInstance().postToMainThread(command); }}; Private static final Executor sIOThreadExecutor = new Executor() {@override public void execute(Runnable command) { getInstance().executeOnDiskIO(command); }}; public void setDelegate(@Nullable TaskExecutor taskExecutor) { mDelegate = taskExecutor == null ? mDefaultTaskExecutor : taskExecutor; } @Override public void executeOnDiskIO(Runnable runnable) { mDelegate.executeOnDiskIO(runnable); } @Override public void postToMainThread(Runnable runnable) { mDelegate.postToMainThread(runnable); } @NonNull public static Executor getMainThreadExecutor() { return sMainThreadExecutor; } @NonNull public static Executor getIOThreadExecutor() { return sIOThreadExecutor; } @Override public boolean isMainThread() { return mDelegate.isMainThread(); }}Copy the code

All of the above methods are implemented directly by proxy classes. So, let’s look at the implementation of the proxy class DefaultTaskExecutor.

Similarly, DefaultTaskExecutor inherits the abstract TaskExecutor class and then implements the abstract methods of the parent class. The complete source code for DefaultTaskExecutor is below

public class DefaultTaskExecutor extends TaskExecutor { private final Object mLock = new Object(); private final ExecutorService mDiskIO = Executors.newFixedThreadPool(2, new ThreadFactory() { private static final String THREAD_NAME_STEM = "arch_disk_io_%d"; private final AtomicInteger mThreadId = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setName(String.format(THREAD_NAME_STEM, mThreadId.getAndIncrement())); return t; }}); @Nullable private volatile Handler mMainHandler; @Override public void executeOnDiskIO(Runnable runnable) { mDiskIO.execute(runnable); } @Override public void postToMainThread(Runnable runnable) { if (mMainHandler == null) { synchronized (mLock) { if (mMainHandler == null) { mMainHandler = new Handler(Looper.getMainLooper()); } } } //noinspection ConstantConditions mMainHandler.post(runnable); } @Override public boolean isMainThread() { return Looper.getMainLooper().getThread() == Thread.currentThread(); }}Copy the code

After the above analysis:

  1. DefaultTaskExecutorinheritanceTaskExecutorAnd implement the superclass method
  2. The internal implementation thread pool Executors. NewFixedThreadPool (int nThreads, ThreadFactory ThreadFactory), soArchTaskExecutorActually, yes.ExecutorsA layer of packaging.
  3. To achieve theHandlerTo switch the task to the main thread.

Conclusion:

ArchTaskExecutor uses proxy mode to implement an internal thread pool, DefaultTaskExecutor, that supports switching the main thread. If the user does not set his or her Own TaskExecutor, ArchTaskExecutor will use DefaultTaskExecutor as the thread pool implemented by default.

If you need to use the thread pool, you don’t need to write a bunch of methods to implement it. You can just call ArchTaskExecutor, and if you don’t, you can define your own TaskExecutor.