Commit 1033fa2b by tonihei Committed by microkatz

Add Util helper methods to work with Futures

This adds two methods that are helpful when working with Futures.
One is a version of postOrRun that can indicate completion by a
Future and the other is a simplified version of Guava's
Futures.transformAsync (which can't be used as it's in Beta).

PiperOrigin-RevId: 461896598
(cherry picked from commit 1df79419)
parent cc5b0e18
...@@ -79,6 +79,11 @@ import com.google.android.exoplayer2.Player; ...@@ -79,6 +79,11 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.Commands; import com.google.android.exoplayer2.Player.Commands;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
...@@ -101,6 +106,8 @@ import java.util.MissingResourceException; ...@@ -101,6 +106,8 @@ import java.util.MissingResourceException;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.regex.Matcher; import java.util.regex.Matcher;
...@@ -602,6 +609,92 @@ public final class Util { ...@@ -602,6 +609,92 @@ public final class Util {
} }
/** /**
* Posts the {@link Runnable} if the calling thread differs with the {@link Looper} of the {@link
* Handler}. Otherwise, runs the {@link Runnable} directly. Also returns a {@link
* ListenableFuture} for when the {@link Runnable} has run.
*
* @param handler The handler to which the {@link Runnable} will be posted.
* @param runnable The runnable to either post or run.
* @param successValue The value to set in the {@link ListenableFuture} once the runnable
* completes.
* @param <T> The type of {@code successValue}.
* @return A {@link ListenableFuture} for when the {@link Runnable} has run.
*/
public static <T> ListenableFuture<T> postOrRunWithCompletion(
Handler handler, Runnable runnable, T successValue) {
SettableFuture<T> outputFuture = SettableFuture.create();
postOrRun(
handler,
() -> {
try {
if (outputFuture.isCancelled()) {
return;
}
runnable.run();
outputFuture.set(successValue);
} catch (Throwable e) {
outputFuture.setException(e);
}
});
return outputFuture;
}
/**
* Asynchronously transforms the result of a {@link ListenableFuture}.
*
* <p>The transformation function is called using a {@linkplain MoreExecutors#directExecutor()
* direct executor}.
*
* <p>The returned Future attempts to keep its cancellation state in sync with that of the input
* future and that of the future returned by the transform function. That is, if the returned
* Future is cancelled, it will attempt to cancel the other two, and if either of the other two is
* cancelled, the returned Future will also be cancelled. All forwarded cancellations will not
* attempt to interrupt.
*
* @param future The input {@link ListenableFuture}.
* @param transformFunction The function transforming the result of the input future.
* @param <T> The result type of the input future.
* @param <U> The result type of the transformation function.
* @return A {@link ListenableFuture} for the transformed result.
*/
public static <T, U> ListenableFuture<T> transformFutureAsync(
ListenableFuture<U> future, AsyncFunction<U, T> transformFunction) {
// This is a simplified copy of Guava's Futures.transformAsync.
SettableFuture<T> outputFuture = SettableFuture.create();
outputFuture.addListener(
() -> {
if (outputFuture.isCancelled()) {
future.cancel(/* mayInterruptIfRunning= */ false);
}
},
MoreExecutors.directExecutor());
future.addListener(
() -> {
U inputFutureResult;
try {
inputFutureResult = Futures.getDone(future);
} catch (CancellationException cancellationException) {
outputFuture.cancel(/* mayInterruptIfRunning= */ false);
return;
} catch (ExecutionException exception) {
@Nullable Throwable cause = exception.getCause();
outputFuture.setException(cause == null ? exception : cause);
return;
} catch (RuntimeException | Error error) {
outputFuture.setException(error);
return;
}
try {
outputFuture.setFuture(transformFunction.apply(inputFutureResult));
} catch (Throwable exception) {
outputFuture.setException(exception);
}
},
MoreExecutors.directExecutor());
return outputFuture;
}
/**
* Returns the {@link Looper} associated with the current thread, or the {@link Looper} of the * Returns the {@link Looper} associated with the current thread, or the {@link Looper} of the
* application's main thread if the current thread doesn't have a {@link Looper}. * application's main thread if the current thread doesn't have a {@link Looper}.
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment