I am trying use rxJava, rxAndroid, Retrofit2, and OkHTTP3 to download a file from a URL endpoint. My code is unable to create the call adapter for an "Observable< retrofit2.Response< okhttp3.ResponseBody>>". These methods are new to me so I believe I'm missing an important concept here. Any direction or points is greatly appreciated.
FATAL EXCEPTION: main Process: com.example.khe11e.rxdownloadfile, PID: 14130 java.lang.IllegalArgumentException: Unable to create call adapter for io.reactivex.Observable> for method RetrofitInterface.downloadFileByUrlRx at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:720) at retrofit2.ServiceMethod$Builder.createCallAdapter(ServiceMethod.java:234) at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:160) at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:166) at retrofit2.Retrofit$1.invoke(Retrofit.java:145) at java.lang.reflect.Proxy.invoke(Proxy.java:393) at $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:5207) at android.view.View$PerformClick.run(View.java:21168) at android.os.Handler.handleCallback(Handler.java:746) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5491) at 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) Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for io.reactivex.Observable>. Tried: * retrofit2.adapter.rxjava.RxJavaCallAdapterFactory * retrofit2.ExecutorCallAdapterFactory at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:237) at retrofit2.Retrofit.callAdapter(Retrofit.java:201) at retrofit2.ServiceMethod$Builder.createCallAdapter(ServiceMethod.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've tried adding Call as mentioned here so it looks like:
Call<Observable<Response<ResponseBody>>> downloadFileByUrlRx(@Url String fileUrl);
however this causes issues with the flatMap function as it cannot find symbol method flatMap(Function< Response< ResponseBody>,Observable< File>>).