我正在使用来自Guava的ListenableFuture,关于它们的一个好处是,将Executor传递给Futures.addCallback
方法,即,要求在给定时执行回调线程/执行人。
在我的Android应用程序中,我希望能够在UI线程中基于ListenableFuture
启动异步执行,并安排一个也在UI线程上执行的回调。因此,我想以某种方式将UI线程执行程序提交到上面提到的Futures.addCallback
方法。如何实现?
或者,换句话说,我想拥有UI线程的执行程序。它是否已经在Android中可用,或者,如果我必须创建自己的,我该怎么做?
编辑:作为此问题的扩展,是否可以做同样的事情,但不仅仅是使用UI线程,而是使用任何特定的线程,在那里调用异步方法?< / p>
我很高兴知道如何在不使用像Handler
和Looper
这样的Android特定内容的情况下实现相同的效果,只需使用纯Java。
答案 0 :(得分:23)
我想我已经看到了一些实现。基本理念大致是
class UiThreadExecutor implements Executor {
private final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable command) {
mHandler.post(command);
}
}
您可以委派在主线程中运行任何东西,方法是将其传递给主线程的处理程序。
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的优势在于它明确表示您使用的Looper
。在你的解决方案中,你可以使用任何调用new ExecuteOnCaller()
的线程的Looper - 而这通常不是你稍后运行代码的线程。
我很乐意知道如何在不使用像Handler和Looper这样的Android特定内容的情况下实现相同的效果,只需使用纯Java。
Looper
,Handler
和所有逻辑背后的消息队列主要由纯Java组成。通用解决方案的问题在于您无法“注入”代码以运行到线程中。线程必须定期检查某种任务队列,看是否有东西要运行。
如果您编写类似
的代码 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!");
}
});
这里没有定义特殊的处理程序,但这是Handler&amp; amp;的核心。 Looper在Android中做。 Android中的Handler
允许您定义Message
的回调,而不仅仅是Runnable
。
Executors.newCachedThreadPool()
和类似做同样的事情。只有多个线程在一个队列中等待代码。
作为这个问题的扩展,是否可以做同样的事情,但不仅仅是使用UI线程,而是使用任何特定的线程,在那里调用异步方法?
通用答案是否定的。只有在有一种方法可以注入要在该线程中运行的代码时。
答案 1 :(得分:7)
基于@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());
答案 2 :(得分:4)
使用com.google.android.gms.tasks.TaskExecutors.MAIN_THREAD
。
使用主应用程序线程的执行程序。
任务API是Google Play服务的一部分Android docs。
答案 3 :(得分:0)
要解决您的问题和扩展问题,以创建仅在当前线程上运行并避免使用Android类的Executor:
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
请参阅文档:https://developer.android.com/reference/java/util/concurrent/Executor
答案 4 :(得分:0)
对于Android UI线程执行器,请使用:
ContextCompat.getMainExecutor(context)