I am trying to convert this working file download code to Reactive. But got stuck, due to my little knowledge of RxJava. Could you help me to make it Reactive?
public void downloadFile(MessageComponent media) {
Request request = new Request.Builder()
.url(media.getMediaUrl())
.build();
Call call = http_client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
Log.e(TAG, "Failed to execute " + request, e);
}
@Override
public void onResponse(Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
String mimeType = MimeTypeMap.getFileExtensionFromUrl(media.getMediaUrl());
File file = new File(helper.getTmpFolder() + "/" + helper.generateUniqueName() + "test." + mimeType);
BufferedSink sink = Okio.buffer(Okio.sink(file));
sink.writeAll(response.body().source());
sink.close();
Log.d(TAG, "downloadFileFromServer done: " + media.getMediaUrl());
}
});
}
This is what I have written so far, and its not getting any result or error:
public void downloadFile(MessageComponent media){
Observable<String> downloadObservable = Observable.create(
sub -> {
Request request = new Request.Builder()
.url(media.getMediaUrl())
.build();
Response response = null;
try {
response = http_client.newCall(request).execute();
if (!response.isSuccessful()) new IOException();
} catch (IOException e) {
e.printStackTrace();
}
sub.onNext(response.toString());
}
);
Subscriber<String> mySubscriber = new Subscriber<String>() {
@Override
public void onNext(String responseString) {
Log.d(TAG, "works: " + responseString);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage(), e);
}
};
downloadObservable
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(mySubscriber);
mySubscriber.unsubscribe();
}
Your code have some errors, which may explain that the expected behaviours is not what you get.
Observable contract error
Reactive Extensions (RxJava is an implemtation of it) is based on this contract : you can be notified multiple times on onNext then, you will notified once (or never...) on an error OR on a completion.
onNext* (onComplete | onError)?
So, your Observable code can be rewriten to this, to emit the fact that your stream is in error or that it is completed.
Observable<String> downloadObservable = Observable.create(
sub -> {
Request request = new Request.Builder()
.url(media.getMediaUrl())
.build();
Response response = null;
response = http_client.newCall(request).execute();
if (response.isSuccessful()) {
sub.onNext(response.toString());
sub.onCompleted();
} else {
sub.onError(new IOException());
}
}
);
Unsubscribe to earlier
You unsubscribe just after your subscription, so your Observable may not have the time to be executed.
downloadObservable
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(mySubscriber);
mySubscriber.unsubscribe();
If the Observable
complete, it will unsubscribe. So won't have to unsubscribe in this case.
This question has the same final goal as the following, which is to download a file and save it to disk in Android:
Downloading files using OkHttp, Okio and RxJava
Download and write a file with Retrofit and RxJava
Here is a good example for downloading a file and save it to disk in Android.
The following is a modification of the above linked example without using the lambda expression.
1.Make sure to include the necessary dependencies in your app build gradle
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.1.0'
2.The Retrofit 2 interface, the @Streaming for downloading large files.
public interface RetrofitApi {
@Streaming
@GET
Observable<Response<ResponseBody>> downloadFile(@Url String fileUrl);
}
3.The code for downloading a file and saving it to disk using Retrofit 2 and rxjava. Update the baseUrl and the url path in the code below to your actual url of the file you need to download.
public void downloadZipFile() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://my.resources.com/")
.client(new OkHttpClient.Builder().build())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
RetrofitApi downloadService = retrofit.create(RetrofitApi.class);
downloadService.downloadFile("resources/archive/important_files.zip")
.flatMap(new Func1<Response<ResponseBody>, Observable<File>>() {
@Override
public Observable<File> call(final Response<ResponseBody> responseBodyResponse) {
return Observable.create(new Observable.OnSubscribe<File>() {
@Override
public void call(Subscriber<? super File> subscriber) {
try {
// you can access headers of response
String header = responseBodyResponse.headers().get("Content-Disposition");
// this is specific case, it's up to you how you want to save your file
// if you are not downloading file from direct link, you might be lucky to obtain file name from header
String fileName = header.replace("attachment; filename=", "");
// will create file in global Music directory, can be any other directory, just don't forget to handle permissions
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsoluteFile(), fileName);
BufferedSink sink = Okio.buffer(Okio.sink(file));
// you can access body of response
sink.writeAll(responseBodyResponse.body().source());
sink.close();
subscriber.onNext(file);
subscriber.onCompleted();
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<File>() {
@Override
public void onCompleted() {
Log.d("downloadZipFile", "onCompleted");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Log.d("downloadZipFile", "Error " + e.getMessage());
}
@Override
public void onNext(File file) {
Log.d("downloadZipFile", "File downloaded to " + file.getAbsolutePath());
}
});
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.