I have an app that uses AlarmManager
to schedule a repeating alarm every X amount of time. When my receiver receives the Intent
, it has to make a http request.
The alarm itself works fine and triggers when it should. The network call, however, starts timing out when the phone is not in use. To be more specific:
When I schedule it to fire every minute (bad practise, I know, but just to illustrate), the first 5-8 minutes the request succeeds. After that, I get a java.net.SocketTimeoutException: connect timed out
. Sometimes it does succeed, but mostly this happens.
I tried setting the connect/read/write timeouts to a minute, but then I get this exception instead of the one above: java.net.ConnectException: Failed to connect to myapp.example.com/123.45.67.89:80
.
My code:
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Consider mApi and myBody to be initialised and valid
mApi.myPostRequest(myBody).enqueue(new Callback<Void> {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
//Does not get here
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
t.printStackTrace();
}
}
}
}
Things I've tried:
WakeLock
in
onReceive
and releasing it when the call is done (added the
permission)Other information:
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), interval, pendingIntent);
from my
Activity
.Any ideas on how to get the network call working when the phone is sleeping?
You Should use JobService here, it has many constraint to handle different scenarios and also your Job is guaranteed to be executed by the system.
The problem here is of doze mode, and using JobService this can be resolved easily.
Implementation is also easy all you need to do is create a JobService and inside it's onStartJob() start your network thread and then just dispatch your job.
For more detail
https://developer.android.com/reference/android/app/job/JobService.html
From https://developer.android.com/reference/android/content/BroadcastReceiver.html
As a general rule, broadcast receivers are allowed to run for up to 10 seconds before they system will consider them non-responsive and ANR the app. Since these usually execute on the app's main thread, they are already bound by the ~5 second time limit of various operations that can happen there (not to mention just avoiding UI jank), so the receive limit is generally not of concern. However, once you use {@goAsync }, though able to be off the main thread, the broadcast execution limit still applies, and that includes the time spent between calling this method and ultimately PendingResult.finish().
Further reading says
If you are taking advantage of this method to have more time to execute, it is useful to know that the available time can be longer in certain situations. In particular, if the broadcast you are receiving is not a foreground broadcast (that is, the sender has not used FLAG_RECEIVER_FOREGROUND), then more time is allowed for the receivers to run, allowing them to execute for 30 seconds or even a bit more.
(long work should be punted to another system facility such as JobScheduler, Service, or see especially JobIntentService),
You can try using @goAsync. Or you can switch your logic to JobIntentService
I haven't tested any of these.
You have a basic mistake in your code - you cannot make requests (or any long running operations) in your broadcast receiver - it dies after ~10 sec so that might be the reason for some of your failures.
You should move the request logic to a service (IntentService) which you'll start from your broadcast receiver and make the request there.
That should work just fine.
From developer docs : https://developer.android.com/reference/android/app/AlarmManager.html
The Alarm Manager holds a CPU wake lock as long as the alarm receiver's onReceive() method is executing. This guarantees that the phone will not sleep until you have finished handling the broadcast. Once onReceive() returns, the Alarm Manager releases this wake lock. This means that the phone will in some cases sleep as soon as your onReceive() method completes
In your code onReceive will return before mApi.myPostRequest(myBody).enqueue ...
task will be executed, then this task probably will be never executed due the CPU will stop as soon onReceive returns.
You said you tested acquiring a WakeLock, but new Android 6.0 Doze mode ignores wakelocks
It seems that OnReceive will have to wait for the task to end
Some ideas:
Checking for some terminate flag in a loop with thread.sleep ?
If the task uses a Thread object then using thread.join() ?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With