I'm using for my Android Service an Handler that reapeat some operation each 60 minutes (1 hour), with a PartialWakeLock to keep the phone not sleeping. But this cause a lot of battery usage.
So a decided to study about AlarmManager (i'm noob) that someone wrote here to be perfect for this kind of things..
But now reading along the web i find that who uses AlarmManager, still need a WakeLock. Is it true?
What is the best way to run a cycle each 60 minutes (1 hour), without kill the battery?
Thanx
P.S.
AlarmManager Android Developer
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.
But so seems that i need 2 wakelock vs just 1 wakelock using handler....is it true?
I have made many test and this is the result:
-Alarm Manager save more battery than using handler+wakelock for long timing operation.
But you must use an additional wake lock to your activity/service started by the alarm, because the alarm manager wake lock doesn't cover it.
Even of this method uses two WakeLock the battery seems to be more efficient and with more life! During the tests (2days) the AlarmManager use 6 time less battery than other method. In my own case...
Hope this can help some one!
I suggest you to use AlarmManager to handle events with 1 hour interval.
Because we don't know exactly what you what to achieve we can't provide a more in deep answer/suggestion sorry.
I am not sure if it is still relevant,
but the answer is: using AlarmManager is preferred. You only need a WakeLock to keep phone running after AlarmManager has woken it up to send an Intent to your receiver and until service has finished its work. So phone will be awake only for a couple of milliseconds, compared to "all the time".
Related
I have a problem (that's why I'm here :P):
I am running a TimerTask and/or a Handler. They should do something every second, no matter if the screen is on or not (standby). The problem is, after some time (2 to 10 hours) this process becomes weird timed. Sometimes it takes 10 seconds, sometimes 4 hours etc.
Now, I've read that you can use a Partial Wake Lock to solve this issue. Tried it, but it has not solved my issue (Maybe you should know that another library is also using a WakeLock which gets released after some time, but mine never gets released by me).
Maybe you should also know that the task/runnable runs on an asynctask (so on it's own thread). The wakelock is created from outside.
Edit:
Maybe it's good to know that it's a device owner app. Also, I know of the battery drain problem, but I still need it. The app really has to process this every second. I just need a solution for it, any, no matter which.
Edit 2:
Here's my current WakeLock code, which is started when my custom application reaches onCreate. As I said, it is never released.:
PowerManager mgr = (PowerManager)getApplicationContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"CustomWakeLock");
wakeLock.acquire();
AlarmManager on API19 has the method setExact() to set an exact alarm.
Exact means --> If I set an alarm to 2:01 pm it will be triggered at 2:01 pm
On API 23 - Marhsmwallow (6.0) there is a new method setExactAndAllowWhileIdle(), but as of the reference it is not EXACT because it will trigger only every minute and in low power idle mode only every 15 minutes.
Exact != every 15 minutes :-)
So how can I achieve an exact alarm with AlarmManager in 6.0?
If a user adds a reminder or a calendar appointment and wants to be informed 10 minutes before the event it should show the alarm EXACT 10 minutes before the event. With setExactAndAllowWhileIdle() this seems is not possible.
Reference Link:
http://developer.android.com/reference/android/app/AlarmManager.html#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent)
So how can I achieve an exact alarm with AlarmManager in 6.0?
You are welcome to try setAlarmClock(), as AFAIK it is unaffected by Doze mode. Otherwise, AlarmManager is not a viable option for you. Even having your app on the battery optimization whitelist will not help, as AlarmManager behavior does not change based on the whitelist.
You are welcome to use GCM, as a high-priority message should give you an opportunity to alert the user. This, of course, requires network connectivity.
The only offline solution that I am aware of — and that I am presently testing — is to have the user add your app to the battery optimization whitelist, then use a foreground service (to try to keep your process around), a ScheduledExecutorService (for the timing), and a partial WakeLock (to keep the CPU on). This will be fairly devastating to the user's battery.
Using setExactAndAllowWhileIdle() for a one-time alarm will fire exactly on the given time even in Doze idle mode. So this probably is the way to go.
Problems start, if you want to repeat the alarm at a rate of < 15 min (or set any other at a time < 15 min away from the last one), as this will not work in Doze idle mode, where such alarms are forced to the next 15 min or are executed when idle maintenance starts, which happens for about ten minutes first after 1 hour, then after another 2 hours, then after another 4 hours and so on.
- EDIT -
As of today Nov 17, Dianne Hackborn writes in this Post's comments:
"For what it's worth, the minimum time between while idle alarms will be changing to 9 minutes at some point relatively soon (even on devices running the current Marshmallow builds)."
This doesn't change anything fundamentally though.
Here are my discussion with Ian Lake on Google+!
setExactAndAllowWhileIdle() is exact and should work.
The 15 minutes time frame is wrong in the java doc.
I was trying to create an automation system running in the background. My frequency range was between 1-15 minutes. My wish was not to use a foreground service. By looking at the name of the method "setExactAndAllowWhileIdle", I thought that yeah it is safe to go with one-time alarms, scheduling the next one when done.
However, I couldn't find a way to run code in doze mode with alarms running more frequent than 15 minutes. Instead, I choose to start a foreground service when doze mode gets activated and stop that foreground service when phone awakes. User won't be seeing your foreground notification while using his/her phone. I don't care much about the ones in doze mode.
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(intent.getAction().equals("android.os.action.DEVICE_IDLE_MODE_CHANGED")){
if (pm.isDeviceIdleMode()) {
//startAutomationForegroundService();
} else {
//stopAutomationForegroundService();
return;
}
AutomationReceiver.completeWakefulIntent(intent);
return;
}
}
You need to register "android.os.action.DEVICE_IDLE_MODE_CHANGED" intent filter into your WakefulBroadcastReceiver. Care putting it into manifest may not help.
I have an Android Application that uses a Countdown Timer that lasts for around 2 days. What is the best method to avoid my Countdown Timer from being killed by the Android Application Manager even if the user enables a power saving mode or restarts their phone ? (Sorry if this is a senseless question to answer, for the reason that I am new to Android development.)
What is the best method to avoid my Countdown Timer from being killed by the Android Application Manager even if the user enables a power saving mode or restarts their phone ?
That is not possible. Moreover, it is very wasteful (tying up system RAM, spending CPU time). If you want to get control at a certain time in the future, use methods on AlarmManager (e.g., setAlarmClock()). If you want to find out how much time remains between now and that certain time in the future, find out the current time (e.g., System.currentTimeMillis()) and subtract that from the future time to calculate the difference in times. To handle a reboot, you would need to set up the AlarmManager again, by using a BroadcastReceiver set up to respond to ACTION_BOOT_COMPLETED.
I need to alert the user of certain events by means of:
Vibration
Notification
The vibration should remain on indefinitely until the user ACKs the notification.
The problem is vibration stops when the device goes to sleep. I've read the following questions:
Allow phone to vibrate when screen turns off
Continue vibration even after the screen goes to sleep mode in Android
There was an answer to one of the above mentioned questions saying that vibrating without patterns did the trick. So I've tried calling the version of Vibrator.vibrate that accepts milliseconds instead of a pattern with a large number but the vibration stops anyway.
Other answers suggest to register a receiver on the ACTION_SCREEN_OFF action. This would allow me to resume vibration if the device goes to sleep after the alarm has started, but won't work if the device was already slept.
However, I could get the thing working if I were able to turn the screen on first, then register the receiver to deal with any screen off event that could happen from there on. So I've tried acquiring a full wake lock when the triggering event is received, before starting sound or vibration, but it does not work despite I'm using the flags FULL_WAKE_LOCK and ACQUIRE_CAUSES_WAKEUP. The wakeup part works, but soon after that the device goes to sleep again. I would like to think the FULL_WAKE_LOCK flag does not work because it has been deprecated in API 17, but my device is a Samsung running 4.1.2 which is API 16!
The recommended approach now seems to be using
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON but this should be called from an activity, and I don't have any screen up unless the user clicks in the notification, and if this happens the sound and vibration should already have been stopped.
So it looks like a dead-end.
What else could I try?
UPDATE:
I had no luck keeping the screen always on with wake locks, but on the other hand they allow me to turn the screen on if only for a few seconds. I actually don't need to keep the screen on, so I'm registering the receiver on the Intent.ACTION_SCREEN_OFF action, and when the screen goes off, the receiver resumes vibration again. This worked well in the Samsung, but I've now switched to a Huawei to continue testing and the receiver does not work.
UPDATE:
Here's the stack trace of the exception in the Huawei device:
java.util.NoSuchElementException: Death link does not exist
at android.os.BinderProxy.unlinkToDeath(Native Method)
at com.android.server.VibratorService.unlinkVibration(VibratorService.java:294)
at com.android.server.VibratorService.removeVibrationLocked(VibratorService.java:284)
at com.android.server.VibratorService.cancelVibrate(VibratorService.java:213)
at android.os.IVibratorService$Stub.onTransact(IVibratorService.java:83)
at android.os.Binder.execTransact(Binder.java:338)
at dalvik.system.NativeStart.run(Native Method)
Do you intend to let the device go to sleep or not? You can acquire a wakelock that wakes the screen on.
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "myTAG");
wl.acquire(LOCK_SCREEN_TIME_MINUTES * 60 * 1000);
That doesn't work for you?
After that you can show the notification, but I'm not sure of the effect, will it hold the vibrations. Above one works on GalaxyTab 2 with android 4.2.2 and HTC Hero with android 2.3.4.
After some testing I finally managed to get it working.
The Vibrator class hass two methods:
vibrate (long[] pattern, int repeat)
vibrate (long milliseconds)
The first one is the only way of vibrating indefinitely using the API (passing 0 as the second argument). But this has been proven to break in some devices (Huawei), as I posted in the question. I'm not talking about the vibration being stopped by the OS when the device goes to sleep, this had been dealt with using a receiver plus a wake lock as described in the question. I'm talking about exceptions, caused by a bugged implementation (the Vibrator class is abstract).
The second variant of this method does not accept a pattern, and does not allow indefinite vibration, but we can cheat this by passing a very large number of milliseconds as parameter. This works well in some devices (Huawei) as the answer I cited in the question correctly pointed, but does not work in others (Samsung), where the implementation has a default max value that will be used instead if the value passed as parameter exceeds it. This max value is actually less than a minute, and this means we can't rely on this approach.
So I went all out on this and created a Service, where I manually vibrate indefinitely like this:
while(vibrationActive){
Vibrator.vibrate(1000);
Thread.sleep(1000);
}
The receiver trick to detect when the screen goes off is no longer needed. Of course the OS keeps shutting down the vibrator when this happens, but the next iteration of the loop will resume vibration again. With this approach it is possible to create a sort of pattern as well, if the sleep time is greater than the vibration time, but again this pattern will be interrupted at any point if the screen goes off.
A dedicated service just to turn the vibrator on and off reliably. Can you believe it? About 150 lines of code (without the unit tests) for something that should have been possible with a few lines.
Disclaimer: My app already working without any Wake Locks for 1+ year and all is well for most devices.
I'm tracking GPS and it works like this:
AlarmReceiver starts Service every 5/10/15 minutes (as user wishes)
Service subscribes for Location updates and waits MAX 1 minute for good GPS.
Wrap up, send data to server and shut down service.
Due to bad connections and bad locations - whole thing take up to 2-3 minutes sometimes. And it works. No matter if phone is sleeping or not.
Now I'm reading about WakeLock and it doesn't make sense to me. How come my stuff is working? Is that coincidence?
How come my stuff is working?
A combination of things, including a dollop of luck. :-)
First, as Joel noted, the device wakes up briefly courtesy of your alarm, but the OS is only guaranteed to hold a WakeLock for the duration of onReceive() of a BroadcastReceiver.
It is possible that, at least on some versions of Android, requesting GPS updates causes the OS to acquire its own WakeLock. This is undocumented behavior AFAIK, and I have never relied upon it personally. If it does, though, and you are doing the rest of your work ("Wrap up, send data to server and shut down service") before removing location updates, that would explain the behavior.
There are still potential gaps in your approach (e.g., if you delegate to a Service to do the work and are not holding a WakeLock as part of passing control to that service). Statistically speaking, it may fail occasionally but work a lot of the time.
Personally, I recommend using a WakeLock, in case the undocumented behavior changes. That's what I do in LocationPoller.
Well reading from the AlarmManager documentation..
The Alarm Manager holds a CPU wake lock as long as the alarm
receiver's onReceive() method is executing.
Further...
Note: The Alarm Manager is intended for cases where you want to have
your application code run at a specific time, even if your application
is not currently running. For normal timing operations (ticks,
timeouts, etc) it is easier and much more efficient to use Handler.
So based on that.. I think it makes sense that it currently works; correct me if I'm wrong.