I would like to know the retry interval for DownloadManager.
My downloads get stuck at STATUS_PAUSED status, with the reason PAUSED_WAITING_TO_RETRY. (This is strange because I have no connection problem and it only happens on Android 6 - please see this separate question for further info).
The app eventually retries to commence the download but the delay is way too long, and the interval seems irregular. When does DownloadManager attempt to retry the download? Is there a way to manually trigger the retry?
One way I can think of going about this is:
Keep track of the id returned on DownloadManager.enqueue
Check for status at intervals you desire and DownloadManager.remove if needed
Unfortunately, as far as I can recollect, the automatic retry cannot be triggered manually.
Hope this helps!
Related
I'm using Retrofit and OkHttpClient to build a Rest API on Android.
Some time ago, i had noticed the very first request made by the api always take way longer to process than all others... At begining i didn't care because it was an acceptable time.
But suddenly the request time jumped to 60 SECONDS
All this time is waste client side, since monitoring the server i see that the processing time takes less than 1 seconds...
I was wondering what change I made that could cause such high impact, then I realized that i changed the connection timeout of OkHttp.
I had changed the value from 10 seconds to 60 seconds just for testings...
I made some experiments setting the connectTimeout to many others values and ALWAYS the first request takes the time sighly above the timeout
Does anyone know what may cause this weird behavior? how to solve it?
PS. I needed to test the api on a desktop and this problem didnt occur, i mean it is only happening on android devices [i tried several] what is the cause?
https://medium.com/inloopx/okhttp-is-quietly-retrying-requests-is-your-api-ready-19489ef35ace
It is likely failing on the first few requests and timing out. OkHTTP is automatically retrying.
builder.retryOnConnectionFailure(false);
Try turning off the auto retry and see what happens.
https://www.reddit.com/r/gfycat/comments/5y2dkm/psa_for_android_devs_how_to_work_around_ipv6/
It is likely an ipv4 vs ipv6 problem. This code prioritizes ipv4 addressses.
You could configure an EventListener in your OkHttpClient and use that to see how the time is spent.
Requirement - I need to get the user's location coordinates every 15 minutes roughly and post it to the server. It is necessary to post data roughly at these intervals.
Implementation - I've made a sync adapter instead of using AlarmManager as it saves battery. I've set ContentResolver.addPeriodicSync() to sync my app every 15 minutes roughly which gets the current location and posts to server.
Problem - In case there's no internet connection, I want to continue taking the user's location every 15 minutes and save them in the local sqlite database. When the internet comes back again next time then I'll post all the saved locations in one go so that server data remains consistent and after that sync will resume as normal.
The main problem is that when there's no internet then the sync stops and I stop getting periodic sync callbacks in my app and I'm not able to save data in the local database. So what I want is that even when there's no internet I keep getting callbacks at regular intervals till the internet comes back and auto sync starts again. Can the sync adapter do that?
One solution I can think of is that I get a broadcast when the Internet stops and at that moment I start using the AlarmManager to start a service every 15 minutes and get the location and save to local database. And when the internet comes back on then I stop using the AlarmManager and go back to auto syncing.
Solution 2 - Provided by David Medenjak below. It is also efficient due to AlarmManager's setInexactRepeating() behavior which tries to imitate Sync adapter's behavior by scheduling Alarms for different apps together to reduce the number of times the CPU wakes up. Also it leads to a little simpler implementation. Would this the better way than the previous solution comparing the pros and cons?
Still any better way to achieve this?
You are mixing two things:
Getting the user location every 15 minutes
Syncing the data with the server
If you start mixing those you have a service and sync adapter that are both strongly dependent on each other, you have to check for states which of those has run and which should run. You might end up with the exact thing that you want (syncing every 15 minutes, just cache it if user is offline) but it will be hard to test and maintain.
Always use a service that is run every 15 minutes to store the current user location.
Periodically sync all updates to the server. This may also happen to be every 15 minutes, but you should not depend on this.
By having one part just storing the location and the other part just synchronizing the data you will have a much easier time handling things. And you also don't have to worry about internet connection or the interval of the synchronizations (since sync adapters are not guaranteed to run at exact times).
Concerning battery life (comments)
There should be no big difference whether a SyncAdapter uses gps and posts it immediately or a service persists it for the time being until the adapter syncs it. As soon as a task has to run every x minutes the device will have to wake up.
There might be slight improvements if the synchronization is run at a slower rate compared to the service, since the gps alone might not need any internet connection.
IntentService - runs every 15 min (using AlarmManager) and saves the user location in the db and mark it as unsent.
SyncAdapter - runs every 15 min and ties to send all unsent locations to the server. On success mark the location as sent. Android will make sure it's only run when there is a internet connection.
Edit:
The key point is separating the two sub-tasks (also suggested by #David Medenjak):
1) Get a location update and store it in a db
2) Send the location updates to the server when there is a network connection.
The FusedLocationProvider has a method
requestLocationUpdates (GoogleApiClient client, LocationRequest request, PendingIntent callbackIntent)
for when your app is in the background. Link
This method is suited for the background use cases, more specifically
for receiving location updates, even when the app has been killed by
the system.
You can use a LocationRequest to set the priority, interval, power consumption. Link
When you receive the pending intent, you can insert the location in the database and request a sync using the sync adapter.
According to the documentation, if download is failed due to http error - the COLUMN_REASON should hold the specific http error code.
the problem I'm having is that in practice, the only reason value I see when download fail is ERROR_HTTP_DATA_ERROR
also, I see in logcat the actual failure http code in runtime, when the download is being stopped and re-try, but I don't see any way to get it from the download manager.
is it possible to get somehow this http code?
I'm using broadcast receiver to handle ACTION_DOWNLOAD_COMPLETE , but I don't see any way to listen to download paused, and I'm getting a feeling that if I'll query download manager failure reason between the retry attempts it does - then I'll get the actual status code.
is it possible to listen to "download pause" event without querying constantly the download manager?
I would expect that will be such broadcast.
the questions that I'd love to finally get answers to are:
is it possible to listen to "download pause" event without querying constantly the download manager, and without active listener to the content resolver?
is download manager (on API level 16+) supports https (ssl) ?
what exactly is download manager retry policy? can I change it default retry policy?
is it possible to get somehow this http code?
Currently, no. The DownloadManager reports a STATUS_SUCCESSFUL even when a download failed, for example because the url/file was not found (HTTP 404) (this is a bug).
Also see DownloadManager sends STATUS_SUCCESSFUL for failed download
I know it's a relatively old thread but that issue still remains. I tested it 5 minutes ago and it still doesn't work properly.
is it possible to listen to "download pause" event without querying constantly the download manager?
Weirdly, no. The only available "events" to listen for are:
To get around this, you have to query the status yourself every X time by checking
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_PAUSED) {
// Do stuff
}
is download manager (on API level 16+) supports https (ssl) ?
It used to not support https (read more in Android DownloadManager and SSL (https)), however it does now. You can simply verify by trying to retrieve a file from a https origin, for example https://mdn.mozillademos.org/files/3794/mixed_content_webconsole.jpg. You will see that it retrieves the file fine.
what exactly is download manager retry policy? can I change it default retry policy?
It is currently not possible to change the retry 'policy'. See the docs and you will find there are no methods or properties regarding this functionality.
Regarding the default retry policy, useful information can be found in the following package: com.android.providers.downloads.Constants. This links to the 5.1.1 version, if you need the information for another version you can manually navigate to that. For example here is the information for android 4.0.1 (the retry policy values are the same as in 5.1.1).
It states:
The number of times that the download manager will retry its network operations when no progress is happening before it gives up.
public static final int MAX_RETRIES = 5;
The minimum amount of time that the download manager accepts for a Retry-After response header with a parameter in delta-seconds.
public static final int MIN_RETRY_AFTER = 30; // 30s
The maximum amount of time that the download manager accepts for a Retry-After response header with a parameter in delta-seconds.
public static final int MAX_RETRY_AFTER = 24 * 60 * 60; // 24h
As you might have guessed, these are final (constant) and thus cannot be changed.
Conclusion: DownloadManager is pretty useful to do some basic downloading, but it's functionality is pretty limited.
I can suggest an alternative: there's a downloadmanager in the android-common libary over at https://github.com/Trinea/android-common
I have not used it myself, but 2k+ stars on github usually means it's worth checking out.
When a network connection is unavailable, the drive sdk takes a very long time to timeout -- it takes 15 mins according to this answer.
I access Drive in an AsynchTask which blocks my application. So, if there is no network connection, the application shows a ProgressDialog for 15 mins, until the time out. Possibly I could implement my own timer which kills the AsynchTask after a reasonable duration. However, it might be difficult to differentiate between a timeout and a lenghty process (like downlaoding a large file on a slow connection) and I would be concenred that there could be issues with the token access not cleaning up properly. Any suggestions for how to work around this?
A check if there is a network connection before performing the AsyncTask would help eliminate a lot of the cases, see Detect whether there is an Internet connection available on Android
I have an application that sends data to a server with a post request. This request can fail, and if it does I want it to retry until it's finally sent, something similar to WhatsApp: if u send a message when u are offline it stays as pendant and when you go online again the message is sent.
Since I don't know how WhatsApp internally works I have some doubts in how to implement that. I thought two ways:
1- Setting an AbstractThreadedSyncAdapter to be executed every X time (like 30 seconds) that checks if there is data to send and, if there is, it sends it to the server.
2- When the user clicks to send some data, I create a thread that tries to send it and, if it fails, it sleeps some seconds and try again.
I really don't like any of these options. The first one is going to increase the battery usage since the application is going to perform operations every X seconds even if it isn't needed. The second one is going to use a lot of battery if the request fails a lot of times.
Is there any better way to do it? It'd be awesome if there was an easy way to detect if the phone has connection to internet.
Thanks!
In your scenario 2, you can set an alarm when the initial post fails to trigger a re-sent some time later. If the send succeeds, you cancel the alarm (or don't schedule another one).
For getting noticed when the device goes online you may look at this answer: https://stackoverflow.com/a/11084311/100957