I'm struggling with AlarmManager.setExact() for couple of days. My application needs exact alarms and works fine on older Androids with AlarmManager.setRepeating().
I've read that it has changed since API 19 and updated my code accordingly.
Here is the code responsible for setting an alarm:
PendingIntent pi = PendingIntent.getBroadcast(context.getApplicationContext(), alarmOrder+1, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getAlarmManager(context).setExact(AlarmManager.RTC_WAKEUP, alarmTimeMillis, pi);
}
else {
getAlarmManager(context).setRepeating(AlarmManager.RTC_WAKEUP, alarmTimeMillis, AlarmManager.INTERVAL_DAY, pi);
}
Broadcast receiver starts new activity with alarm screen:
#Override
public void onReceive(Context context, Intent intent) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AlarmTimeReceiver");
wl.acquire();
Intent alarmIntent = new Intent(context, AlarmActivity.class);
alarmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(alarmIntent);
//Release the lock
wl.release();
}
The thing is that while testing on Android 4.4 device connected via USB cable all works fine. Alarm starts on specified time all the time (with screen turned on/off). Unfortunately, when I disconnect device from computer, alarm starts correctly only when screen is turned on. When I turn screen off, alarms starts in inexact time.
Have I missed something? Have any of you experienced similar situation?
It runs correctly on Android < 19
Not really.
A _WAKEUP alarm guarantees -- via a framework-managed WakeLock -- that the device will stay awake until onReceive() returns. Then, the framework releases the WakeLock, and the device can fall back asleep... if there are no other outstanding WakeLocks.
Your WakeLock, as written, is pointless. It merely duplicates the framework-managed WakeLock without adding value.
startActivity() is asynchronous. The activity will not be anywhere near started by the time onReceive() ends and the framework-managed WakeLock (and your additional one) are released. Now, sometimes, your activity will get a chance to start up anyway, because the device does not fall back asleep quickly. I presume that you are using android:keepScreenOn or the equivalent in the activity, so once you get to that point, there is another WakeLock outstanding, and the device cannot fall back asleep automatically.
However, sometimes the device will indeed fall back asleep before your activity starts up. That might have changed with Android 5.0 -- it would not surprise me in the least if Android is more aggressive about putting the device back to sleep more quickly. So, while your previous approach might have worked 90% of the time, it may be a lot less now. However, your previous approach did not work 100% of the time.
We see the same thing with services. In fact, this scenario is a lot more common there. That's why, back in April 2009, I created the WakefulIntentService, and why in August 2013, Google released WakefulBroadcastReceiver. Both offer a pattern for acquiring the WakeLock in onReceive() but then not releasing it until the service's work is finished.
Neither of those solutions will work out of the box for you, as both are tied inextricably to services. However, you can use those as a source of ideas for rolling your own. You need to move your WakeLock into a static data member (ick), and only release() it once your activity is far enough along that it has its own WakeLock. For example, if you are calling setKeepScreenOn() on a View in Java code, once that is done, I would expect that it is safe to release your original WakeLock. Then, the framework can take over and release the keep-screen-on WakeLock based upon user input.
Related
I've read just about every Stackoverflow answer that exists on this topic, but none of them worked.
Goal: Keep my service running 24/7, all the time
Problem: Whenever my device is on sleep mode for an hour or more, the service is killed
What I've tried to fix it:
Returning START_STICKY from onStartCommand() and using startForeground()
public int onStartCommand(Intent intent, int flags, int startId) {
notification = makeStickyNotification(); //I've simplified the irrelevant code, obviously this would be a real notification I build
startForeground(1234, notification);
return START_STICKY;
}
This works fine, and it even restarts my service whenever the device is low on memory, but it is not enough to fix the problem that occurs when my device goes to sleep for a while.
Using Alarm Manager in onCreate() of my Activity and in onStartCommand() of my Service to call a Broadcast Receiver that calls my service
Intent ll24 = new Intent(this, AlarmReceiver.class);
PendingIntent recurringLl24 = PendingIntent.getBroadcast(this, 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000*60, recurringLl24); // Every minute
This helps keep my service active, but again, doesn't solve my problem
Using Schedule Task Executor to keep it alive
if (scheduleTaskExecutor == null) {
scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
scheduleTaskExecutor.scheduleAtFixedRate(new mainTask(), 0, 1, TimeUnit.SECONDS);
}
...
class mainTask implements Runnable {
public void run() {
// 1 Second Timer
}
}
This also just keeps the service active but doesn't keep it alive after a long sleep.
Separate task Manifest
android:launchMode="singleTop"
This did nothing
How can I (1) test this issue without having to put my phone to sleep and check every hour and (2) keep my service running despite the device going to sleep?
Your service was killed by Doze or Standby mode of Android. That was introduced in Android 6.0 (API level 23).
Doze restrictions
The following restrictions apply to your apps while in Doze:
Network access is suspended.
The system ignores wake locks.
Standard AlarmManager alarms (including setExact() and setWindow()) are deferred to the next maintenance window.
If you need to set alarms that fire while in Doze, use setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.
The system does not perform Wi-Fi scans.
The system does not allow sync adapters to run. The system does not allow JobScheduler to run.
So system ignored your Alarm Clocks, Scheduler, etc.
In Android Oreo release Android defined limits to background services.
To improve the user experience, Android 8.0 (API level 26) imposes
limitations on what apps can do while running in the background.
Still if app need to run its service always, then we can create foreground service.
Background Service Limitations: While an app is idle, there are limits
to its use of background services. This does not apply to foreground
services, which are more noticeable to the user.
So create a foreground service. In which you will put a notification for user while your service is running. See this answer (There are many others)
Now what if you don't want a notification for your service. A solution is for that.
You can create some periodic task that will start your service, service will do its work and stops itself. By this your app will not be considered battery draining.
You can create periodic task with Alarm Manager, Job Scheduler, Evernote-Jobs or Work Manager.
Instead of telling pros & cons of each one. I just tell you best. Work manager is best solution for periodic tasks. Which was introduced with Android Architecture Component.
Unlike Job-Scheduler(only >21 API) it will work for all versions.
Also it starts work after a Doze-Standby mode.
Make a Android Boot Receiver for scheduling service after device boot.
I created forever running service with Work-Manager, that is working perfectly.
The murder mystery has been solved, and I know what killed my service. Here's what I did:
After I realized that startsticky, startforeground, alarmmanager, scheduleTaskExecutor, and even wakelock were unable to save my service, I realized the murderer couldn't be the Android system, because I had taken every measure possible to prevent the system from killing my service and it still would get killed.
I realized I needed to look for another suspect, since the service wasn't dying because of the system. For that, I had to run an investigation. I ran the following command:
adb shell dumpsys activity processes > tmp.txt
This would give me a detailed log of all the processes running and their system priorities. Essentially, tmp.txt would be the detective in this murder mystery.
I looked through the file with lots of detail. It looked like my service was prioritized properly by the system:
Proc #31: adj=prcp /FS trm= 0 2205:servicename.service/uID (fg-service)
The above line indicates the exact priority of a process running on the Android device. adj=prcp means the service is a visible foreground service.
At this point, I realized that my service must be encountering some error a couple hours after running, so I let it run and die. After it died, I produced a dumpsys again to examine the error:
At this point, my service wasn't listed as a task in the tmp.txt file. Excited, I scrolled to the bottom of the dumpsys and solved the mystery!
com.curlybrace.ruchir.appName.MyService$2.onForeground(MyService.java:199)
at com.rvalerio.fgchecker.AppChecker$2.run(AppChecker.java:118)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
The stack trace that caused the killing of my service was displayed right there! Essentially, a variable that would check for the foreground app being used would become null after a few hours of inactivity, which would cause an exception, and kill the service!
Key Takeaways:
If your service is getting killed, and you've done everything you can to make sure that it shouldn't be killed, perform a dumpsys and examine the nitty gritty of your device's activity process. I guarantee you will find the issue that way.
I still would like to have the bounty awarded to #Khemraj since his answer could be a great solution for someone who hasn't started their service properly. However, I am accepting this answer since it is the solution that actually fixed the issue.
onDestroy() is really unreliable and won't be called often that you want. Same for onLowMemory() callbacks. There is no way to take a guaranteed callback if android decides to kill your process or if user decides to Force Stop your app.
That's normal that than user device go to sleep mode, your service dies. Read about wakelocks. Try something like that in your service:
In manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
In service:
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"tag");
wakeLock.acquire();
But it's rly tricky for user and totally anti-pattern in android world, cuz of battery consumption.
Another option is to trigger service something like every 10 mins. Make pending intent on WakefulBroadcastReceiver(where you can start your service) and schedule it with alarm manager with flag RTC_WAKE_UP
Starting from SDK 26 a Service should have its relative "MainActivity" in foreground OR this Service should be started as in foreground using "startForegroundService()". The "startForeground()" doesn't work as expected if the target SDK is 26+ but need the other way I just explained.
After this you can use following code to Kill and restart the App from scratch (yes, even the Service is killed in this way):
Intent mStartActivity = new Intent(context, StartActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
Doze mode kills services to save battery. Only valid solution for you is to create a foreground service in Oreo and above.
https://developer.android.com/about/versions/oreo/background
My IntentService gets fired from 2 places, either by an Alarm or by the Activity and since the duration is related to the amount of data it needs do fetch from the web, from what I understood, I need to keep a partial wakelock.
Here's my implementation:
#Override
protected void onHandleIntent(Intent intent) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakeLock");
try {
wakeLock.setReferenceCounted(false);
wakeLock.acquire(3600000);
///other code here
}
catch{
}
finally{
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
My question is: will this work good enough? Will the finally make sure that the wakelock is released in any circumstances? From what I know onHandleIntent handles intent one after another, so there is no risk in having 2 intents/2 wakelocks in the same time.
Later edit:
The IntentService is called in 2 ways:
from my Activity, like
startService(new Intent(context, MyService.class).putExtra()..);
2 from a triggered Alarm using a PendingIntent
PendingIntent pendingIntent = PendingIntent.getService(context, someId, myServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Will the service have enough time to aquire wakelock when ran from the Alarm?
Whether you need to keep wake-lock or not should not be related to the amount of work your Service does - theoretically, device can go to sleep even if the amount of work is small.
Wake-locks should be considered only if you absolutely must ensure that device can't sleep while the Service is running. Cases like this are very rare. Some examples:
Alarm clock applications (need to wake you up even if the device is sleeping)
Real time messaging applications (need to notify you about new messages even if the device is sleeping)
Most applications don't have such a strict timing requirements. For example, the following are NOT good reasons to use wake locks:
Periodic synchronization of data with the server (should be delayed until device awakes)
Displaying current user's location on map (can be obtained when device awakens; but wake-lock will be needed for applications that monitor user's entire route)
If you really need to ensure that the device doesn't sleep during Service execution, then you need to acquire a wake-lock (one of the several types). Let's assume that this is the case here.
You want to be able to start the "wakeful" Service from application's UI (Activity), and using AlarmManager.
Starting from UI
Since the device should be completely awake in order for the user to interact with UI, you can safely assume that if you start the Service in response to UI interaction it will have a chance to acquire a wake-lock (but do it as soon as the Service is started).
Your solution covers this case.
Starting from AlarmManager
Unfortunately, there is no guarantee (at least no documented guarantee) that when AlarmManager starts the Service it will hold a wake lock and allow the Service to acquire its own wake-lock. This means that the device can go to sleep after alarm fired, but before your Service had a chance to acquire the wake-lock.
This means that your solution will "break" in this case.
The only documented scheme in which AlarmManager will help you with wake-locks involves broadcasts:
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. If your alarm receiver called
Context.startService(), it is possible that the phone will sleep
before the requested service is launched. To prevent this, your
BroadcastReceiver and Service will need to implement a separate wake
lock policy to ensure that the phone continues running until the
service becomes available.
This is where WakefulBroadcastReceiver comes in very handy.
Note that if you use this scheme, then there is no need to support a different one for "UI initiated" case - use the same approach in both cases.
You might also want to take a look at this library developed by #CommonsWare (I didn't use it myself though).
* Updated: The issue has likely been resolved. Please refer directly to the update below, as AlarmManager was only partially to blame. *
I'm currently developing an android app with alarm clock functionality. Unfortunately it turned out that there seem to be rather specific cases for which AlarmManager doesn't seem to work as expected .
When I initially tested the app, I did so by means of the Android Studio Emulator (Nexus 5, API 21) as well as an old phone of mine (Galaxy S2, API 16), with the result that all alarms were delivered in time. As soon as I switched
to my Xperia Z1 Compact (API 19) though, alarms suddenly triggered minutes late occasionally.
Interestingly this seems to be the case especially when the phone is currently running on battery (i.e. not connected to the pc or an outlet). It kind of feels like AlarmManager would suddenly act super sluggish in a desperate attempt to spare the battery, completely oblivious to the fact that it was utilized by means of .setExact(). If the device is not asleep, delivery is always in time.
Either way, the behaviour that results from my code doesn't seem to be deterministic at all, which is what really boggles my mind.
Simplified version of my code:
First, I schedule AlarmManager depending on the API. According to the logs calendar is set to the correct date and time.
public abstract class AlarmScheduler {
//...
private static void schedule(AlarmManager am, PendingIntent pi, Calendar calendar){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
}
}
//...
}
The pending intent that is scheduled is a WakefulBroadcastReceiver. According to the logs, every time the alarm is late the call of
onReceive() is as well, at least as far as I've seen. So it seems likely that the problem is located here.
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, AlarmService.class);
service.putExtras(intent);
startWakefulService(context, service);
}
}
For completeness, this is the service that is started by the BroadcastReceiver. The activity that the service starts acquires its own wakelock in onResume(). I've also tried to forcefully delay the release of the receiver wakelock up to 1000 ms, in order to guarantee that one is active at all times, but that didn't yield different results.
public class AlarmService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent alarmDisplay = new Intent(getBaseContext(), AlarmActivity.class);
alarmDisplay.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmDisplay.putExtras(intent);
getApplication().startActivity(alarmDisplay);
/** Some simple database operations here */
AlarmReceiver.completeWakefulIntent(intent);
return super.onStartCommand(intent, flags, startId);
}
}
I'm honestly kind of lost where I did go wrong here. But since an alarm clock that might trigger minutes late on some devices is not acceptable of course, I'd
really appreciate any input - thanks in advance.
UPDATE
Alright, so first of all AlarmManager.setExact() does indeed seem to deliver late occasionally, but only for seconds, not minutes. According to the documentation this seems to be intended, as it states:
"The alarm will be delivered as nearly as possible to the requested trigger time."
Unfortunately this does sound like a matter of milliseconds rather than seconds, which is why I did assume that AlarmManager was behaving faultily in the first place . If it was evidently delivering seconds late, I thought it capable of delivering minutes late as well. Still, my mistake ultimately.
Secondly, the issue described initially was a wakelock issue that, at least in my case, did only show on my API 19+ phone when it was currently running on battery. While API 19 apparently introduced very aggressive power management for the sake of battery life, the management appears to be even more aggressive if the phone is running on battery. This seems also to be the reason why on the emulator everything was behaving normal (per default it is always in the process of being charged, if the icons are to be believed).
The problem with the code initially posted is that the receiver wakelock is released before the activity acquires its own wakelock in onResume(). Just a matter of milliseconds, but still sufficient for the CPU to be pushed back to sleep apparently. As I did state in my original post, I already thought of this as a potential source of the problem, which is why I did try to forcefully delay the release of the wakelock, in order to guarantee that at least one would be active at all times. For testing purposes I did this quick & dirty by means of wait() though, which the OS seemingly wasn't very fond of and thus forcefully shut down my service at some point, which was something that didn't show in regular logcat output. From this point on subsequent alarms tended to be faulty, although I'm not sufficiently knowledgeable in terms of Android to actually explain why.
Either way, the issue could be resolved by acquiring and releasing only one single wakelock instead of two separate ones (please mind that, due to the very nature of the problem, there is no absolute certainty, but testing results are looking fine).
I set the alarm like this:
registerReceiver(wakeUpReceiver, new IntentFilter("com.example.yay"));
pi = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("com.example.yay"), 0);
am = (AlarmManager)(getSystemService(Context.ALARM_SERVICE));
if (android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+30*1000, pi);
} else {
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+30*1000, pi);
}
And it works if the device is awake, but if I turn off the screen, it doesn't turn on the screen (but wakeUpReceiver's onReceive() gets called)
What do I need to put in the manifest? AlarmManager docs don't seem to mention anything relevant to permissions or intent filter or anything like that. Maybe I'm just misunderstanding the docs as usual?
And yes, I've read all the other similar questions, but they're not helping.
You can use Wakelock to achieve this:
Different Levels Wake Locks:
FULL_WAKE_LOCK - Keep the screen at full brightness, keyboard
back-light illuminated, and the CPU running. SCREEN_BRIGHT_WAKE_LOCK -
Keeps the screen at full brightness and the CPU running.
SCREEN_DIM_WAKE_LOCK - Keeps the screen ON but lets it dim and the CPU
running. PARTIAL_WAKE_LOCK - Keeps the CPU running
Android permission for Wakelock:
Since wake locks has huge impact on battery life your application needs to request WAKE_LOCK permission in order to create them.
Create Wake Locks
To create a wake lock call newWakeLock on the Power manger as follows:
PowerManager.WakeLock wakeLock ;
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK, "My wakelook");
Release Wake lock
wakeLock.release();
Its very important to release wakelock or else it will drain the battery.
As answered above use wakelock create WakeLock in onReceive().
Or I suggest you to use WakefulBroadcastReceiver.
This will make sure that ur phone is awake during service.
WakefulBroadcastReceiver is the best option for the 2nd thing, about wakelocks - u can check on google some examples that show how to use alarm manager with wakelocks.
Ofc, ur service can done it's work, but sometimes sleep can occour, so I suggest you to implement it even if ur tests don't show the problem, better too keep it safe.
Are you sure onReceive() being called? Create some logs and do some tests. For now there can be 2 problems: or sleep is not calling onReceive() (here check the power managment options on your phone, I had problem with "Stamina" on my phone, which was blocking my alarms) or your phone is falling asleep before u do your work (then wakelocks/WakefulBroadcastReceiver).
Also AlarmManager will not "light up" ur screen, only CPU.
To turn on the screen use getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
or android:keepScreenOn="true" in layout.
The method suggested by another user SCREEN_DIM_WAKE_LOCK was deprecated in API level 17, keep it in mind!
For more:
https://developer.android.com/training/scheduling/wakelock.html
I am using broadcast receiver with AlarmManager, but sometimes it takes lot of seconds to wake up, which causes issue in app functionality
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
wl.acquire();
and releases after launching activity
I am thinking to change it.
Can you tell what is the best approach?
use broadcast receiver to perform some calculations, and then starts activity (wake lock problem so far in my case).
Start Activity directly, and do calculations weather to continue this activity or terminate before setting content view
I am using broadcast receiver with AlarmManager, but sometimes it takes lot of seconds to wake up, which causes issue in app functionality
I am not aware that any other sort of component will somehow start up faster than a BroadcastReceiver. Certainly I would expect an activity to start up more slowly than does a BroadcastReceiver.
and releases after launching activity
The WakeLock is doing you no good, as there already is a WakeLock in place for the duration of your onReceive() call (assuming that this is a manifest-registered BroadcastReceiver).
The only way this WakeLock will be useful is if you release it from onCreate() of the activity, to try to keep the device awake long enough for the activity to start up.
Can you tell what is the best approach?
That depends on what the "calculations" are. onReceive() is called on the main application thread. Like all methods called on the main application thread, you want onReceive() to return in a couple of milliseconds. If your "calculations" will definitely take only a couple of milliseconds, stick with the BroadcastReceiver. If the "calculations" will take longer than that, you will want to consider delegating that work to an IntentService, and have the IntentService start the activity if needed.
With respect to the "lot of seconds", bear in mind that your AlarmManager events may be inexact, depending on what method you use to set them up, what your targetSdkVersion is, and what version of Android you are running on.