Android 中 SingleThreadExecutor 和 Handler 之间的区别(通过示例)

Difference between a SingleThreadExecutor and a Handler in Android (by Example)

我正在研究一本教科书示例,介绍如何使用 Android 中的 Room 将数据写入 SQlite 数据库。显然,我们不能从主线程将数据写入数据库,但为此我们需要一个后台线程。在我的书中,我们所做的是,我们创建一个额外的 class AppExecutors,内容如下:

public class AppExecutors {
    private final Executor _diskIO;
    private final Executor _networkIO;
    private final Executor _mainThread;

    private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
        _diskIO = diskIO;
        _networkIO = networkIO;
        _mainThread = mainThread;
    }

    public AppExecutors() {
        this(Executors.newSingleThreadExecutor(),
                Executors.newFixedThreadPool(3),
                new MainThreadExecutor());
    }

    public Executor diskIO() {
        return _diskIO;
    }


public Executor networkIO() {
        return _networkIO;
    }

    public Executor mainThread() {
        return _mainThread;
    }

    private static class MainThreadExecutor implements Executor {
        private Handler mainThreadHandler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(Runnable command) {
            mainThreadHandler.post(command);
        }
    }
}

然后,在包含 UI 的 MainActivity 中,即点击时触发动作的按钮,我们调用

getApp().getExecutors().diskIO().execute(() -> {
    ...
}

其中 getApp().getExecutors() returns AppExecutors.

的新实例

这是我的问题:

我明白 getApp().getExecutors().mainThread().execute(() -> {...} 传递了一个新的 Runnable 并且这个 Runnable 被提供给主线程的消息队列。 Looper从消息队列中取出每个线程并执行。

但是,getApp().getExecutors().diskIO().execute(() -> {...} 的情况有所不同。 传递给它的 Runnable 显然是在后台线程中执行的。 Executors.newSingleThreadExecutor() 好像开新帖了。但是没有与该线程关联的处理程序或消息队列。那么,我就在想,这个线程执行完不会被关闭吗?一旦线程关闭,我们就不能再打开同一个线程。这让我有点困惑,线程不应该保持打开状态吗? 我现在已经阅读了 ExecutorService,显然这个服务只是让线程保持打开状态。但是话又说回来,这不是消息请求和处理程序正在做的事情吗?那么这有什么不同呢?

我认为 this answer 会部分回答您的问题。另外对于你的问题:

So, I was wondering, won't this thread be closed after execution? Once the thread is closed, we cannot open the same thread again. And that's a bit confusing for me, shouldn't the thread stay open?

不,不会。该执行程序服务的线程将处于空闲或阻塞状态,直到您将新的可运行对象排队,因为它使用 BlockingQueue。如果队列为空,线程将被阻塞,如果 Runnale 对象到达其队列,线程将被激活。如您所见,ExecutorService 没有 Looper。仅当您调用 ExecutorService 的 shutdown() 或 shutdownNow() 方法时,它才会被销毁。

But then again, isn't this the same thing that the messageque and the handler are doing? So how is this different?

相比之下,Handler 需要绑定到 Looper 才能发送消息和 post Runnable,Looper 位于 HandlerThread 中。一个HandlerThread对应一个单线程的executor服务,它的Handler对应一个Executor。在 AppExecutors class 中,一个 Handler 绑定到主线程的 Looper,因为 UI 对象不能从创建它的主线程以外的线程访问。

让我们看看示例 Java 代码中的区别。 使用 ExecutorService 的后台线程示例。

/*
To create a background thread making use of the Java concurrent API first we
need to create an ExecutorService or only Executor if you don't want to manage
the service. But generally it must be shutdown when we are done with it.
That's why we need a reference to the ExecutorService in orderto shutdown it.
*/
ExecutorService executorService = Executors.newSingleThreadExecutor();
Executor executor = executorService;

// To send a command:
executor.execute(someRunnable);
// As you can see an executor has only the execute method.

// After we are done with executor service we must shutdown it.
executorService.shutdown();
// or
executorService.shutdownNow();
// This returns a list of runnables that were in the queue.

使用 HandlerThread 的后台线程示例:

/*
To create a background thread which has a message queue we have to make
use of the Android's HandlerThread and Handler API. A HandlerThread object
manages a Looper and a Thread in it while A Handler object is used to send
commands or messages to a living HandlerThread object.
*/
// A HandlerThread can also be instantiated with a name and a priority.
HandlerThread handlerThread = new HandlerThread();
handlerThread.start(); // Must call start in contras to an ExecutorService

// Tie this handler to the handlerThread's Looper so that we can send
// commands or messages to its queue.
Handler handler = new Handler(handlerThread.getLooper());

// To send a command
handler.post(someRunnable); // or
handler.postDelayed(someRunnable, delayInMillis); // or
handler.postAtFrontOfQueue(someRunnable); // or
handler.postAtTime(someRunnable, upTimeInMillis);

Handler API 只是 Android 特定的 API 并且有许多实用程序来发送和处理 Message 对象和 Runnable 命令。有关详细信息,请参阅 Handler API。

这是一个修改后的 AppExecutors 的例子 class。您可以看到,我们没有使用单线程执行器,而是使用了后台处理程序线程,我们创建了一个 Handler 对象,然后将其绑定到它的 HandlerThread 对象。 此外,对于主线程执行,我们删除了包装器 class 并创建了另一个 Handler 对象,然后将其绑定到主线程的循环程序。我希望一切对你来说更清楚

public class AppExecutors {
    private final Handler _diskIO;
    private final Executor _networkIO;
    private final Handler _mainThread;

    private AppExecutors(Handler diskIO, Executor networkIO, Handler mainThread) {
        _diskIO = diskIO;
        _networkIO = networkIO;
        _mainThread = mainThread;
    }

    public AppExecutors() {

        HandlerThread htDiskIO = new HandlerThread(
            "com.application.htDiskIO", // Give it a name (optional)
            Process.THREAD_PRIORITY_BACKGROUND
        );

        this( new Handler(htDiskIO.getLooper()),
              Executors.newFixedThreadPool(3),
              new Handler(Looper.getMainLooper()));
    }

    public Handler diskIO() {
        return _diskIO;
    }


    public Executor networkIO() {
        return _networkIO;
    }

    public Handler mainThread() {
        return _mainThread;
    }
}

这是新 AppExecutors 的一些示例用法片段class:

AppExecutors executors = new AppExecutors();

// Now for diskIO operations you must call like:
executors.diskIO().post(someRunnable);
executors.diskIO().postDelayed(someRunnable, someDelay);

// The network operation is same
executors.networkIO().execute(someRunnable);

// And last but not least; main thread operations
execute.mainThread().post(someRunnable);
execute.mainThread().postDelayed(someRunnable, someDelay);

还有一点要注意,HandlerThread 和 Handler API 属于 Google 的 android.os 包。 因此,您将无法在不是为 Android OS 编写的应用程序中使用它们。 本例就到此为止,祝你学业有成