Skip to content

Commit f0cad11

Browse files
authored
Merge pull request #2962 from square/jakew/completable-future-android/2018-11-16
Enable built-in CompletableFuture adapter on Android API 24+
2 parents bc62069 + 5ae6568 commit f0cad11

File tree

3 files changed

+82
-24
lines changed

3 files changed

+82
-24
lines changed

retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,14 @@
1515
*/
1616
package retrofit2;
1717

18-
import java.io.IOException;
1918
import java.lang.annotation.Annotation;
2019
import java.lang.reflect.ParameterizedType;
2120
import java.lang.reflect.Type;
2221
import java.util.concurrent.CompletableFuture;
2322
import javax.annotation.Nullable;
2423
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
2524

26-
/**
27-
* A {@linkplain CallAdapter.Factory call adapter} which creates Java 8 futures.
28-
* <p>
29-
* Adding this class to {@link Retrofit} allows you to return {@link CompletableFuture} from
30-
* service methods.
31-
* <pre><code>
32-
* interface MyService {
33-
* &#64;GET("user/me")
34-
* CompletableFuture&lt;User&gt; getUser()
35-
* }
36-
* </code></pre>
37-
* There are two configurations supported for the {@code CompletableFuture} type parameter:
38-
* <ul>
39-
* <li>Direct body (e.g., {@code CompletableFuture<User>}) returns the deserialized body for 2XX
40-
* responses, sets {@link retrofit2.HttpException HttpException} errors for non-2XX responses, and
41-
* sets {@link IOException} for network errors.</li>
42-
* <li>Response wrapped body (e.g., {@code CompletableFuture<Response<User>>}) returns a
43-
* {@link Response} object for all HTTP responses and sets {@link IOException} for network
44-
* errors</li>
45-
* </ul>
46-
*/
47-
@IgnoreJRERequirement
25+
@IgnoreJRERequirement // Only added when CompletableFuture is available (Java 8+ / Android API 24+).
4826
final class CompletableFutureCallAdapterFactory extends CallAdapter.Factory {
4927
static final CallAdapter.Factory INSTANCE = new CompletableFutureCallAdapterFactory();
5028

retrofit/src/main/java/retrofit2/Platform.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import javax.annotation.Nullable;
2929
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
3030

31+
import static java.util.Arrays.asList;
3132
import static java.util.Collections.emptyList;
3233
import static java.util.Collections.singletonList;
3334
import static java.util.Collections.unmodifiableList;
@@ -147,7 +148,14 @@ static class Android extends Platform {
147148
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
148149
@Nullable Executor callbackExecutor) {
149150
if (callbackExecutor == null) throw new AssertionError();
150-
return singletonList(new ExecutorCallAdapterFactory(callbackExecutor));
151+
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
152+
return Build.VERSION.SDK_INT >= 24
153+
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
154+
: singletonList(executorFactory);
155+
}
156+
157+
@Override int defaultCallAdapterFactoriesSize() {
158+
return Build.VERSION.SDK_INT >= 24 ? 2 : 1;
151159
}
152160

153161
@Override List<? extends Converter.Factory> defaultConverterFactories() {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (C) 2016 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package retrofit2;
17+
18+
import java.util.concurrent.CompletableFuture;
19+
import okhttp3.mockwebserver.MockResponse;
20+
import okhttp3.mockwebserver.MockWebServer;
21+
import org.junit.Before;
22+
import org.junit.Rule;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.robolectric.RobolectricTestRunner;
26+
import org.robolectric.annotation.Config;
27+
import retrofit2.helpers.ToStringConverterFactory;
28+
import retrofit2.http.GET;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
import static org.junit.Assert.fail;
32+
import static org.robolectric.annotation.Config.NEWEST_SDK;
33+
34+
@RunWith(RobolectricTestRunner.class)
35+
@Config(sdk = NEWEST_SDK)
36+
public final class CompletableFutureAndroidTest {
37+
@Rule public final MockWebServer server = new MockWebServer();
38+
39+
interface Service {
40+
@GET("/") CompletableFuture<String> endpoint();
41+
}
42+
43+
private Service service;
44+
45+
@Before public void setUp() {
46+
Retrofit retrofit = new Retrofit.Builder()
47+
.baseUrl(server.url("/"))
48+
.addConverterFactory(new ToStringConverterFactory())
49+
.build();
50+
service = retrofit.create(Service.class);
51+
}
52+
53+
@Config(sdk = 24)
54+
@Test public void completableFutureApi24() throws Exception {
55+
server.enqueue(new MockResponse().setBody("Hi"));
56+
57+
CompletableFuture<String> future = service.endpoint();
58+
assertThat(future.get()).isEqualTo("Hi");
59+
}
60+
61+
@Config(sdk = 21)
62+
@Test public void completableFuturePreApi24() {
63+
try {
64+
service.endpoint();
65+
fail();
66+
} catch (IllegalArgumentException e) {
67+
assertThat(e).hasMessage(
68+
"Unable to create call adapter for java.util.concurrent.CompletableFuture<java.lang.String>\n"
69+
+ " for method Service.endpoint");
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)