final FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)
.setDownloadConcurrentLimit(3)
.enableRetryOnNetworkGain(true)
.enableAutoStart(false)
.setProgressReportingInterval(1100)
.setHttpDownloader(getOkHttpDownloader())
.build();
Fetch.Impl.setDefaultInstanceConfiguration(fetchConfiguration);
I often face "Download fail" even for direct links. Both when single as well as multiple downloads. When we manually resume download, it resumes. So is there any inbuilt options to automate retry?
Or any workaround?
@jpvs0101 There is no build in option to retry a failed download. If a download fails then the idea behind it is it may fail again in a future. That is why there is no automatic retry for failed downloads in the library. A download can fail for so many reasons that the library cannot handle them all. That is why the errors are returned to the user of the library to handle. Also an auto retry option on a failed download may cause a cycle of retries not giving newer downloads a chance to process and download. I have not really thought of any solution beyond this point. Suggestions are welcomed!
Actually I solved it myself with simple trick.
1) Store retry count (mutable extras) inside download request.
2) When download failed -> if (count < 10) {count ++; retry;}
(Note: When user manually click resume button, then i reset count =0)
My code
@Override
public void onError(@NotNull Download download, @NotNull Error error, @org.jetbrains.annotations.Nullable Throwable throwable) {
final int retryAttempts = download.getExtras().getInt(KEY_RETRY_COUNT, 0) ;
if (retryAttempts < MAX_RETRY_COUNT) {
final MutableExtras extras = download.getExtras().toMutableExtras();
extras.putInt(KEY_RETRY_COUNT, retryAttempts + 1 );
fetch.replaceExtras(download.getId(), extras, result -> fetch.retry(download.getId()), null);
} else {
showDownloadFailedNotification(download);
}
}
....
private void resumeOrRetryDownload(int downloadId) {
fetch.getDownload(downloadId, result -> {
if (null != result) {
// Reset retry count, because this 'resumeOrRetryDownload()' is by user click option
final MutableExtras extras = result.getExtras().toMutableExtras();
extras.putInt(KEY_RETRY_COUNT, 0 );
fetch.replaceExtras(downloadId, extras, result1 -> {
if (result.getStatus() == Status.PAUSED)
fetch.resume(downloadId); // for paused ones
else
fetch.retry(downloadId); // for failed ones
}, null);
}
});
That simple.
But you can make it more easy by add an built in variable to download request like below.
final FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)
.setMaxRetries(10); // global default limit
.setAutoRetryFailedDownload(true); // global
....
download.setMaxRetries(5); // overrides global limit
download.setAutoRetryFailedDownload(true);
....
download.resetRetryCounter(); // this is important, bcz when user manually click resume button, we should try another 5 times
Actually you already gave us ways to achieve auto retry. But if you make it as an inbuilt option, it will clean up some biolerplate code!
Is that cool?
@jpvs0101 Let me look into this and I will follow up.
@jpvs0101 Reviewed your solution. The issue I see is that if this is implemented in the library, the library will retry the failed download right away still causing the starvation of downloads that were enqueued last. Whiles this is no issue for fetch handling a small amount of downloads. Imagine a batch of 1000 or more download each having to retry 10 times. It could be a while before other downloads are processed.
Fetch handles the processing of downloads based on priority and time created. These fields on the download/request would have to be manipulated for each failed download to prevent the starvation issue and the user may no be expecting these fields to change or they may depend on the original values.
Downloads could fail for a number of reasons. I think the library already handles this well by informing the user in a callback and having them decide when to retry.
But I guess it is better to have an built-in option for retry. It is fundamental like pause / resume. Because the developer know these basics when it comes 1000 downloads. Auto retry provides better end user experience. That's important. Of course it depends on how we implement, so all the things. I can say that my app is much better after I implement auto retry myself.
I started a huge file download when I was travelling. It is direct link.
Before I implement auto-retry: It fails several times because of network failure where poor network areas. So every time I have to take my mobile to make sure the download is going. Ahhh...
After I implement auto-retry: Started download, I took a nap, when I woke up & check the download, cool, it is finished!
We can give the option to end user, so they can decide whether on/off, like most download managers. For developers, it is optional & you can provide the above comment as documentation so that they can be conscious about what they are doing.
Anyone can use my above implementation, nothing complex, but if it is an built in option, it eliminates some biolerplate code.
@jpvs0101 great use case. Let me take a second look into this.
@jpvs0101 Just released Fetch version 3.0.5 with this new feature. Also released fetch version 3.0.6 See change log and readme.
Awesome, give me few days to test it...
Edit 1: @tonyofrancis Seems like working good!
onError() will be called only after all retry attempts failed! That has sense! You may mention that in docs.request.setAutoRetryMaxAttempts() override global limit?request1.setAutoRetryMaxAttempts(2), after that i call fetchConfig.setAutoRetryMaxAttempts(6) then request1.getAutoRetryMaxAttempts() will return 6. Right?request1.getAutoRetryMaxAttempts() it will return 2. That is what was set on it. fetchConfig.setAutoRetryMaxAttempts(6) is global and its a different setting. The download object now has two fields. autoRetryMaxAttempts which will return the same as the request. The other field is autoRetryAttempts which will indicate how many times the library attempted to download the file. So in the case the global limit was set to 10 and the max on the request was set to 3. The library will attempt up to 10 times and the autoRetryAttempts will always reflect that.Reset retry attempts is master piece!
Thank you very much!
Awesome work!