Skip to content

Commit

Permalink
Merge pull request #2962 from square/jakew/completable-future-android…
Browse files Browse the repository at this point in the history
…/2018-11-16

Enable built-in CompletableFuture adapter on Android API 24+
  • Loading branch information
swankjesse committed Nov 16, 2018
2 parents bc62069 + 5ae6568 commit f0cad11
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,14 @@
*/
package retrofit2;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;

/**
* A {@linkplain CallAdapter.Factory call adapter} which creates Java 8 futures.
* <p>
* Adding this class to {@link Retrofit} allows you to return {@link CompletableFuture} from
* service methods.
* <pre><code>
* interface MyService {
* &#64;GET("user/me")
* CompletableFuture&lt;User&gt; getUser()
* }
* </code></pre>
* There are two configurations supported for the {@code CompletableFuture} type parameter:
* <ul>
* <li>Direct body (e.g., {@code CompletableFuture<User>}) returns the deserialized body for 2XX
* responses, sets {@link retrofit2.HttpException HttpException} errors for non-2XX responses, and
* sets {@link IOException} for network errors.</li>
* <li>Response wrapped body (e.g., {@code CompletableFuture<Response<User>>}) returns a
* {@link Response} object for all HTTP responses and sets {@link IOException} for network
* errors</li>
* </ul>
*/
@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();

Expand Down
10 changes: 9 additions & 1 deletion retrofit/src/main/java/retrofit2/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -147,7 +148,14 @@ static class Android extends Platform {
@Override List<? extends CallAdapter.Factory> 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<? extends Converter.Factory> defaultConverterFactories() {
Expand Down
72 changes: 72 additions & 0 deletions retrofit/src/test/java/retrofit2/CompletableFutureAndroidTest.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> 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<java.lang.String>\n"
+ " for method Service.endpoint");
}
}
}

0 comments on commit f0cad11

Please sign in to comment.