RxAndroid and Retrofit: unable to create a call adapter for io.reactivex.Observable <retrofit2.Response <okhttp3.ResponseBody >>
I am trying to use rxJava, rxAndroid, Retrofit2 and OkHTTP3 to download a file from a URL endpoint. My code cannot create a call adapter for "Observable <retrofit2.Response <okhttp3.ResponseBody →". These methods are new to me, so I believe that an important concept is missing here. Any direction or points are welcome.
FATAL EXCEPTION: main Process: file com.example.khe11e.rxdownload, PID: 14130 java.lang.IllegalArgumentException: unable to create a call adapter for io.reactivex.Observable> for the RetrofitInterface.downloadFileByUrlRx method in retrofit2.ServiceMethodror $ .java: 720) in retrofit2.ServiceMethod $ Builder.createCallAdapter (ServiceMethod.java:234) in retrofit2.ServiceMethod $ Builder.build (ServiceMethod.java:160) on retrofit2.Retrofit.loadServiceMethod (Retrofit.java:166) .Retrofit $ 1.invoke (Retrofit.java:145) in java.lang.reflect.Proxy.invoke (Proxy.javahaps93) in $ Proxy0.downloadFileByUrlRx (Unknown source) at com.example.khe11e.rxdownloadfile.MainActivity.downloadImage (MainActivity.java:46) at com.example.khe11e.rxdownloadfile.MainActivity $ 1.onClick (MainActivity.java:39) at android.view.View.performClick (View.java∗207) on android.view.View $ PerformClick.run (View.java:21168) on android.os.Handler.handleCallback (Handler.java:746) on android.os.Handler.dispatchMessage (Handler.java:95) on android.os .Looper.loop (Looper.java:148) at android.app.ActivityThread.main (ActivityThread.java<491) in java.lang.reflect.Method.invoke (native method) at com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:728) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:618) Called: java.lang.IllegalArgumentException: Could not find the call adapter for io.reactivex.Observable> . Tried: * retrofit2.adapter.rxjava.RxJavaCallAdapterFactory * retrofit2.ExecutorCallAdapterFactory on retrofit2.Retrofit.nextCallAdapter (Retrofit.java:237) on retrofit2.Retrofit.callAdapter (Retrofit.java:201Serc .java: 232) ... 16 more
build.gradle:
compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.0.4' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
RetrofitInterface.java:
package com.example.khe11e.rxdownloadfile; import io.reactivex.Observable; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Response; import retrofit2.http.GET; import retrofit2.http.Streaming; import retrofit2.http.Url; public interface RetrofitInterface { // Retrofit 2 GET request for rxjava @Streaming @GET Observable<Response<ResponseBody>> downloadFileByUrlRx(@Url String fileUrl); }
MainActivity.java:
package com.example.khe11e.rxdownloadfile; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import java.io.File; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; import okhttp3.OkHttpClient; import okhttp3.ResponseBody; import okio.BufferedSink; import okio.Okio; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; public class MainActivity extends AppCompatActivity { Button downloadImgBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); downloadImgBtn = (Button) findViewById(R.id.downloadImgBtn); downloadImgBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { downloadImage(); } }); } public void downloadImage(){ RetrofitInterface downloadService = createService(RetrofitInterface.class, "https://www.nasa.gov/"); downloadService.downloadFileByUrlRx("sites/default/files/iss_1.jpg") .flatMap(processResponse()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(handleResult()); } public <T> T createService(Class<T> serviceClass, String baseUrl){ Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .client(new OkHttpClient.Builder().build()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build(); return retrofit.create(serviceClass); } public Function<Response<ResponseBody>, Observable<File>> processResponse(){ return new Function<Response<ResponseBody>, Observable<File>>() { @Override public Observable<File> apply(Response<ResponseBody> responseBodyResponse) throws Exception { return saveToDiskRx(responseBodyResponse); } }; } private Observable<File> saveToDiskRx(final Response<ResponseBody> response){ return Observable.create(new ObservableOnSubscribe<File>() { @Override public void subscribe(ObservableEmitter<File> subscriber) throws Exception { String header = response.headers().get("Content-Disposition"); String filename = header.replace("attachment; filename=", ""); new File("/data/data/" + getPackageName() + "/images").mkdir(); File destinationFile = new File("/data/data/" + getPackageName() + "/images/" + filename); BufferedSink bufferedSink = Okio.buffer(Okio.sink(destinationFile)); bufferedSink.writeAll(response.body().source()); bufferedSink.close(); subscriber.onNext(destinationFile); subscriber.onComplete(); } }); } private Observer<File> handleResult(){ return new Observer<File>() { @Override public void onSubscribe(Disposable d) { Log.d("OnSubscribe", "OnSubscribe"); } @Override public void onNext(File file) { Log.d("OnNext", "File downloaded to " + file.getAbsolutePath()); } @Override public void onError(Throwable e) { e.printStackTrace(); Log.d("Error", "Error " + e.getMessage()); } @Override public void onComplete() { Log.d("OnComplete", "onCompleted"); } }; } }
I tried adding Call as mentioned here to look like this:
Call<Observable<Response<ResponseBody>>> downloadFileByUrlRx(@Url String fileUrl);
however, this causes problems with the flatMap function because it cannot find the flatMap character method (Function <Response <ResponseBody>, Observable <File →).
You are using the RxJava 1 adapter for retooling, replace it with the RxJava 2 option:
//compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
UPDATE
Starting with the retrofit version 2.2.0
for RxJava2, there is an adapter for the first side:
compile 'com.squareup.retrofit2:retrofit:2.2.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
Update: June / 2019
Rxjava version updated to 2.2.10
compile 'io.reactivex.rxjava2:rxandroid:2.1.1' compile 'io.reactivex.rxjava2:rxjava:2.2.10' compile 'com.squareup.retrofit2:retrofit:2.6.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.6.0'
Also, upgrades require at least Java 7 or Android 2.3
------------------------------------------
For beginners (July / 2017):
You messed up your version of the library for sure.
I am using the latest version of RXAndroid 2.0.1
compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.0' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
1) Make sure you have the same Retrofit and Retrofit-RxJava adapters.
2) Use compilation
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
not
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
and use RxJava2CallAdapterFactory.create()
instead of RxJavaCallAdapterFactory.create()
when creating the upgrade.