捕获当前线程的执行程序

2022-09-05 00:05:25

我正在使用来自Guava的ListenableFuture,它们的一个好处是,将Executor传递给方法,即要求在给定的线程/执行器上执行回调。Futures.addCallback

在我的Android应用程序中,我希望能够基于UI线程启动异步执行,并计划一个回调,该回调也在UI线程上执行。因此,我想以某种方式将UI线程执行器提交给上述方法。如何做到这一点?ListenableFutureFutures.addCallback

或者,换句话说,我希望有一个UI线程的执行器。它是否已经在Android中可用,或者,如果我必须创建自己的,我该怎么做?

编辑:作为这个问题的扩展,是否可以做同样的事情,但不仅仅是使用UI线程,而是使用任何特定的线程,其中调用了异步方法?

我很乐意知道如何实现相同的效果,而无需诉诸于Android特定的东西,例如和,只是使用纯Java。HandlerLooper


答案 1

我想我已经看到一些实现这样做。基本思想大致是

class UiThreadExecutor implements Executor {
    private final Handler mHandler = new Handler(Looper.getMainLooper());

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

您可以通过将主线程传递给主线程的处理程序来委派运行主线程中的任何内容。

编辑:例如 https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit/android/MainThreadExecutor.java

Edit2:您可以配置处理程序,例如 SensorManager#registerListener(...,Handler handler) 允许您这样做。

class HandlerThreadExecutor implements Executor {
    private final Handler mHandler;
    public HandlerThreadExecutor(Handler optionalHandler) {
        mHandler = optionalHandler != null ? optionalHandler : new Handler(Looper.getMainLooper());
    }

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

与使用当前线程的循环器相比,它的优点是它明确了您使用哪个。在您的解决方案中,您采用任何线程调用的Looper - 这通常不是您稍后运行代码的线程。Loopernew ExecuteOnCaller()

我很乐意知道如何实现相同的效果,而无需诉诸于Android特定的东西,如Handler和Looper,只是使用纯Java。

Looper,而所有这些逻辑背后的消息队列主要由纯Java组成。通用解决方案的问题在于,您无法“注入”代码以运行到线程中。线程必须定期检查某种任务队列,以查看是否有要运行的内容。Handler

如果你写这样的代码

    new Thread(new Runnable() {
        @Override
        public void run() {
            while (!Thread.interrupted()) {
                System.out.println("Hello");
            }
        }
    }).start();

然后没有办法让该线程做任何其他事情,而是不断打印“Hello”。如果可以做到这一点,就像在程序代码中动态插入到其他代码的跳转一样。这将是一个可怕的想法。

    final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    Runnable codeToRunInThisThread = queue.take();
                    codeToRunInThisThread.run();
                }
            } catch (InterruptedException ignored) {}
        }
    }).start();

另一方面是一个简单的线程,它在队列上永远循环。线程可以在两者之间执行其他任务,但您必须在代码中添加手动检查。

您可以通过以下方式向其发送任务

    queue.put(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello!");
        }
    });

这里没有定义特殊的处理程序,但这是Herlage & Looper在Android中的核心。 在 Android 中,您可以为 a 定义回调,而不仅仅是 .HandlerMessageRunnable

Executors.newCachedThreadPool()和类似的做大致相同的事情。单个队列中只有多个线程等待代码。


作为这个问题的扩展,是否可以做同样的事情,但不仅仅是使用UI线程,而是使用任何特定的线程,其中调用了异步方法?

一般的答案是否定的。仅当有一种方法可以注入代码以在该线程中运行时。


答案 2

基于@zapl的asnwer,这是我的实现,它也回答了编辑(扩展)的问题:https://gist.github.com/RomanIakovlev/8540439

弄清楚我也会把它放在这里,以防万一链接有一天会腐烂:

package com.example.concurrent;

import android.os.Handler;
import android.os.Looper;

import java.util.concurrent.Executor;

/**
* When the calling thread has a Looper installed (like the UI thread), an instance of ExecuteOnCaller will submit
* Runnables into the caller thread. Otherwise it will submit the Runnables to the UI thread.
*/
public class ExecuteOnCaller implements Executor {

    private static ThreadLocal<Handler> threadLocalHandler = new ThreadLocal<Handler>() {
        @Override
        protected Handler initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null)
            looper = Looper.getMainLooper();
            return new Handler(looper);
        }
    };

    private final Handler handler = threadLocalHandler.get();

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

我使用它的模式是这样的:

/**
* in SomeActivity.java or SomeFragment.java
*/
Futures.addCallback(myModel.asyncOperation(param), new FutureCallback<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            // handle success
        }

        @Override
        public void onFailure(Throwable throwable) {
            // handle exception
        }
    }, new ExecuteOnCaller());

推荐