Skip to content

Commit f68ceb4

Browse files
Michiel VermeerschJakeWharton
authored andcommitted
Added RxJava3 adapter
1 parent 1078fd1 commit f68ceb4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4870
-0
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ buildscript {
2222
'androidxTestRunner': 'androidx.test:runner:1.1.0',
2323
'rxjava': 'io.reactivex:rxjava:1.3.8',
2424
'rxjava2': 'io.reactivex.rxjava2:rxjava:2.0.0',
25+
'rxjava3': 'io.reactivex.rxjava3:rxjava:3.0.0',
2526
'reactiveStreams': 'org.reactivestreams:reactive-streams:1.0.3',
2627
'scalaLibrary': 'org.scala-lang:scala-library:2.13.1',
2728
'gson': 'com.google.code.gson:gson:2.8.5',

retrofit-adapters/rxjava3/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
RxJava3 Adapter
2+
==============
3+
4+
An `Adapter` for adapting [RxJava 3.x][1] types.
5+
6+
Available types:
7+
8+
* `Observable<T>`, `Observable<Response<T>>`, and `Observable<Result<T>>` where `T` is the body type.
9+
* `Flowable<T>`, `Flowable<Response<T>>` and `Flowable<Result<T>>` where `T` is the body type.
10+
* `Single<T>`, `Single<Response<T>>`, and `Single<Result<T>>` where `T` is the body type.
11+
* `Maybe<T>`, `Maybe<Response<T>>`, and `Maybe<Result<T>>` where `T` is the body type.
12+
* `Completable` where response bodies are discarded.
13+
14+
15+
Usage
16+
-----
17+
18+
Add `RxJava3CallAdapterFactory` as a `Call` adapter when building your `Retrofit` instance:
19+
```java
20+
Retrofit retrofit = new Retrofit.Builder()
21+
.baseUrl("https://example.com/")
22+
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
23+
.build();
24+
```
25+
26+
Your service methods can now use any of the above types as their return type.
27+
```java
28+
interface MyService {
29+
@GET("/user")
30+
Observable<User> getUser();
31+
}
32+
```
33+
34+
By default, `create()` will produce reactive types which execute their HTTP requests asynchronously
35+
on a background thread. There are two other ways to control the threading on which a request
36+
occurs:
37+
38+
* Use `createSynchronous()` and call `subscribeOn` on the returned reactive type with a `Scheduler`
39+
of your choice.
40+
* Use `createWithScheduler(Scheduler)` to supply a default subscription `Scheduler`.
41+
42+
Download
43+
--------
44+
45+
Download [the latest JAR][2] or grab via [Maven][3]:
46+
```xml
47+
<dependency>
48+
<groupId>com.squareup.retrofit2</groupId>
49+
<artifactId>adapter-rxjava3</artifactId>
50+
<version>latest.version</version>
51+
</dependency>
52+
```
53+
or [Gradle][3]:
54+
```groovy
55+
implementation 'com.squareup.retrofit2:adapter-rxjava3:latest.version'
56+
```
57+
58+
Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap].
59+
60+
61+
62+
[1]: https://github.com/ReactiveX/RxJava/tree/3.x
63+
[2]: https://search.maven.org/remote_content?g=com.squareup.retrofit2&a=adapter-rxjava3&v=LATEST
64+
[3]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.squareup.retrofit2%22%20a%3A%22adapter-rxjava3%22
65+
[snap]: https://oss.sonatype.org/content/repositories/snapshots/
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apply plugin: 'java-library'
2+
apply plugin: 'com.vanniktech.maven.publish'
3+
4+
dependencies {
5+
api project(':retrofit')
6+
api deps.rxjava3
7+
api deps.reactiveStreams
8+
compileOnly deps.findBugsAnnotations
9+
10+
testImplementation deps.junit
11+
testImplementation deps.assertj
12+
testImplementation deps.guava
13+
testImplementation deps.mockwebserver
14+
}
15+
16+
jar {
17+
manifest {
18+
attributes 'Automatic-Module-Name': 'retrofit2.adapter.rxjava3'
19+
}
20+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
POM_ARTIFACT_ID=adapter-rxjava3
2+
POM_NAME=Adapter: RxJava 3
3+
POM_DESCRIPTION=A Retrofit CallAdapter for RxJava 3's stream types.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (C) 2020 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.adapter.rxjava3;
17+
18+
import io.reactivex.rxjava3.core.Observable;
19+
import io.reactivex.rxjava3.core.Observer;
20+
import io.reactivex.rxjava3.disposables.Disposable;
21+
import io.reactivex.rxjava3.exceptions.CompositeException;
22+
import io.reactivex.rxjava3.exceptions.Exceptions;
23+
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
24+
import retrofit2.Response;
25+
26+
final class BodyObservable<T> extends Observable<T> {
27+
private final Observable<Response<T>> upstream;
28+
29+
BodyObservable(Observable<Response<T>> upstream) {
30+
this.upstream = upstream;
31+
}
32+
33+
@Override
34+
protected void subscribeActual(Observer<? super T> observer) {
35+
upstream.subscribe(new BodyObserver<>(observer));
36+
}
37+
38+
private static class BodyObserver<R> implements Observer<Response<R>> {
39+
private final Observer<? super R> observer;
40+
private boolean terminated;
41+
42+
BodyObserver(Observer<? super R> observer) {
43+
this.observer = observer;
44+
}
45+
46+
@Override
47+
public void onSubscribe(Disposable disposable) {
48+
observer.onSubscribe(disposable);
49+
}
50+
51+
@Override
52+
public void onNext(Response<R> response) {
53+
if (response.isSuccessful()) {
54+
observer.onNext(response.body());
55+
} else {
56+
terminated = true;
57+
Throwable t = new HttpException(response);
58+
try {
59+
observer.onError(t);
60+
} catch (Throwable inner) {
61+
Exceptions.throwIfFatal(inner);
62+
RxJavaPlugins.onError(new CompositeException(t, inner));
63+
}
64+
}
65+
}
66+
67+
@Override
68+
public void onComplete() {
69+
if (!terminated) {
70+
observer.onComplete();
71+
}
72+
}
73+
74+
@Override
75+
public void onError(Throwable throwable) {
76+
if (!terminated) {
77+
observer.onError(throwable);
78+
} else {
79+
// This should never happen! onNext handles and forwards errors automatically.
80+
Throwable broken =
81+
new AssertionError(
82+
"This should never happen! Report as a bug with the full stacktrace.");
83+
//noinspection UnnecessaryInitCause Two-arg AssertionError constructor is 1.7+ only.
84+
broken.initCause(throwable);
85+
RxJavaPlugins.onError(broken);
86+
}
87+
}
88+
}
89+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (C) 2020 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.adapter.rxjava3;
17+
18+
import io.reactivex.rxjava3.core.Observable;
19+
import io.reactivex.rxjava3.core.Observer;
20+
import io.reactivex.rxjava3.disposables.Disposable;
21+
import io.reactivex.rxjava3.exceptions.CompositeException;
22+
import io.reactivex.rxjava3.exceptions.Exceptions;
23+
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
24+
import retrofit2.Call;
25+
import retrofit2.Callback;
26+
import retrofit2.Response;
27+
28+
final class CallEnqueueObservable<T> extends Observable<Response<T>> {
29+
private final Call<T> originalCall;
30+
31+
CallEnqueueObservable(Call<T> originalCall) {
32+
this.originalCall = originalCall;
33+
}
34+
35+
@Override
36+
protected void subscribeActual(Observer<? super Response<T>> observer) {
37+
// Since Call is a one-shot type, clone it for each new observer.
38+
Call<T> call = originalCall.clone();
39+
CallCallback<T> callback = new CallCallback<>(call, observer);
40+
observer.onSubscribe(callback);
41+
if (!callback.isDisposed()) {
42+
call.enqueue(callback);
43+
}
44+
}
45+
46+
private static final class CallCallback<T> implements Disposable, Callback<T> {
47+
private final Call<?> call;
48+
private final Observer<? super Response<T>> observer;
49+
private volatile boolean disposed;
50+
boolean terminated = false;
51+
52+
CallCallback(Call<?> call, Observer<? super Response<T>> observer) {
53+
this.call = call;
54+
this.observer = observer;
55+
}
56+
57+
@Override
58+
public void onResponse(Call<T> call, Response<T> response) {
59+
if (disposed) return;
60+
61+
try {
62+
observer.onNext(response);
63+
64+
if (!disposed) {
65+
terminated = true;
66+
observer.onComplete();
67+
}
68+
} catch (Throwable t) {
69+
Exceptions.throwIfFatal(t);
70+
if (terminated) {
71+
RxJavaPlugins.onError(t);
72+
} else if (!disposed) {
73+
try {
74+
observer.onError(t);
75+
} catch (Throwable inner) {
76+
Exceptions.throwIfFatal(inner);
77+
RxJavaPlugins.onError(new CompositeException(t, inner));
78+
}
79+
}
80+
}
81+
}
82+
83+
@Override
84+
public void onFailure(Call<T> call, Throwable t) {
85+
if (call.isCanceled()) return;
86+
87+
try {
88+
observer.onError(t);
89+
} catch (Throwable inner) {
90+
Exceptions.throwIfFatal(inner);
91+
RxJavaPlugins.onError(new CompositeException(t, inner));
92+
}
93+
}
94+
95+
@Override
96+
public void dispose() {
97+
disposed = true;
98+
call.cancel();
99+
}
100+
101+
@Override
102+
public boolean isDisposed() {
103+
return disposed;
104+
}
105+
}
106+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (C) 2020 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.adapter.rxjava3;
17+
18+
import io.reactivex.rxjava3.core.Observable;
19+
import io.reactivex.rxjava3.core.Observer;
20+
import io.reactivex.rxjava3.disposables.Disposable;
21+
import io.reactivex.rxjava3.exceptions.CompositeException;
22+
import io.reactivex.rxjava3.exceptions.Exceptions;
23+
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
24+
import retrofit2.Call;
25+
import retrofit2.Response;
26+
27+
final class CallExecuteObservable<T> extends Observable<Response<T>> {
28+
private final Call<T> originalCall;
29+
30+
CallExecuteObservable(Call<T> originalCall) {
31+
this.originalCall = originalCall;
32+
}
33+
34+
@Override
35+
protected void subscribeActual(Observer<? super Response<T>> observer) {
36+
// Since Call is a one-shot type, clone it for each new observer.
37+
Call<T> call = originalCall.clone();
38+
CallDisposable disposable = new CallDisposable(call);
39+
observer.onSubscribe(disposable);
40+
if (disposable.isDisposed()) {
41+
return;
42+
}
43+
44+
boolean terminated = false;
45+
try {
46+
Response<T> response = call.execute();
47+
if (!disposable.isDisposed()) {
48+
observer.onNext(response);
49+
}
50+
if (!disposable.isDisposed()) {
51+
terminated = true;
52+
observer.onComplete();
53+
}
54+
} catch (Throwable t) {
55+
Exceptions.throwIfFatal(t);
56+
if (terminated) {
57+
RxJavaPlugins.onError(t);
58+
} else if (!disposable.isDisposed()) {
59+
try {
60+
observer.onError(t);
61+
} catch (Throwable inner) {
62+
Exceptions.throwIfFatal(inner);
63+
RxJavaPlugins.onError(new CompositeException(t, inner));
64+
}
65+
}
66+
}
67+
}
68+
69+
private static final class CallDisposable implements Disposable {
70+
private final Call<?> call;
71+
private volatile boolean disposed;
72+
73+
CallDisposable(Call<?> call) {
74+
this.call = call;
75+
}
76+
77+
@Override
78+
public void dispose() {
79+
disposed = true;
80+
call.cancel();
81+
}
82+
83+
@Override
84+
public boolean isDisposed() {
85+
return disposed;
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)