From 5ae656821500940b97ecc30a99d2f1472678b4d6 Mon Sep 17 00:00:00 2001 From: Jake Wharton Date: Fri, 16 Nov 2018 15:29:56 -0500 Subject: [PATCH] Enable built-in CompletableFuture adapter on Android API 24+ --- .../CompletableFutureCallAdapterFactory.java | 24 +------ .../src/main/java/retrofit2/Platform.java | 10 ++- .../CompletableFutureAndroidTest.java | 72 +++++++++++++++++++ 3 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 retrofit/src/test/java/retrofit2/CompletableFutureAndroidTest.java diff --git a/retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java b/retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java index f0d82849b1..537c16412d 100644 --- a/retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java +++ b/retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java @@ -15,7 +15,6 @@ */ package retrofit2; -import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -23,28 +22,7 @@ import javax.annotation.Nullable; import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; -/** - * A {@linkplain CallAdapter.Factory call adapter} which creates Java 8 futures. - *

- * Adding this class to {@link Retrofit} allows you to return {@link CompletableFuture} from - * service methods. - *


- * interface MyService {
- *   @GET("user/me")
- *   CompletableFuture<User> getUser()
- * }
- * 
- * There are two configurations supported for the {@code CompletableFuture} type parameter: - * - */ -@IgnoreJRERequirement +@IgnoreJRERequirement // Only added when CompletableFuture is available (Java 8+ / Android API 24+). final class CompletableFutureCallAdapterFactory extends CallAdapter.Factory { static final CallAdapter.Factory INSTANCE = new CompletableFutureCallAdapterFactory(); diff --git a/retrofit/src/main/java/retrofit2/Platform.java b/retrofit/src/main/java/retrofit2/Platform.java index dcaf317e91..52f7557bd1 100644 --- a/retrofit/src/main/java/retrofit2/Platform.java +++ b/retrofit/src/main/java/retrofit2/Platform.java @@ -28,6 +28,7 @@ import javax.annotation.Nullable; import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static java.util.Collections.unmodifiableList; @@ -147,7 +148,14 @@ static class Android extends Platform { @Override List defaultCallAdapterFactories( @Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); - return singletonList(new ExecutorCallAdapterFactory(callbackExecutor)); + ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor); + return Build.VERSION.SDK_INT >= 24 + ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory) + : singletonList(executorFactory); + } + + @Override int defaultCallAdapterFactoriesSize() { + return Build.VERSION.SDK_INT >= 24 ? 2 : 1; } @Override List defaultConverterFactories() { diff --git a/retrofit/src/test/java/retrofit2/CompletableFutureAndroidTest.java b/retrofit/src/test/java/retrofit2/CompletableFutureAndroidTest.java new file mode 100644 index 0000000000..1561a145b6 --- /dev/null +++ b/retrofit/src/test/java/retrofit2/CompletableFutureAndroidTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit2; + +import java.util.concurrent.CompletableFuture; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import retrofit2.helpers.ToStringConverterFactory; +import retrofit2.http.GET; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.robolectric.annotation.Config.NEWEST_SDK; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = NEWEST_SDK) +public final class CompletableFutureAndroidTest { + @Rule public final MockWebServer server = new MockWebServer(); + + interface Service { + @GET("/") CompletableFuture endpoint(); + } + + private Service service; + + @Before public void setUp() { + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(server.url("/")) + .addConverterFactory(new ToStringConverterFactory()) + .build(); + service = retrofit.create(Service.class); + } + + @Config(sdk = 24) + @Test public void completableFutureApi24() throws Exception { + server.enqueue(new MockResponse().setBody("Hi")); + + CompletableFuture future = service.endpoint(); + assertThat(future.get()).isEqualTo("Hi"); + } + + @Config(sdk = 21) + @Test public void completableFuturePreApi24() { + try { + service.endpoint(); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage( + "Unable to create call adapter for java.util.concurrent.CompletableFuture\n" + + " for method Service.endpoint"); + } + } +}