I am developing a native android app that run a backup operation every 30 mins.
I am using AlarmManager for this purpose and it works fine. Here is the code I am using to start the alarm:
public static void startSync(Context context) {
alarmIntent = new Intent(context, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// int interval = 3600000;
int interval =30000 ;
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
Toast.makeText(context, "Sync Started", Toast.LENGTH_SHORT).show();
}
And here is the the on receive method:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent arg1) {
PowerManager pm = (PowerManager) context.getSystemService(context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
Intent eventService = new Intent(context, SyncInBackground.class);
context.startService(eventService);
wl.release();
}
}
I noticed that when my device isn't in standby, the operation took 5 seconds (I calculated it programmatically) but when the mobile is in standby mode it took 11 seconds. That's why I used wake_lock before running the backup operation in a background service, in order to make the app takes only 5 seconds.
But I still get the same results if the mobile in standby mode... it still takes 11 seconds and 5 seconds if not in standby mode.
What can I do to make my background service run the repeating alarm in 5 seconds instead of 11 seconds?
The usual mistake: acquiring a wake lock in the OnReceive does nothing. The AlarmManager already holds a wake lock in OnReceive. Your way works out of pure luck, when/if it works. You have to either use a WakefulBroadcastReceiver or use a WakefulIntentService. The WIS will acquire a static wake lock which will be active between OnReceive returning and the service starting.
See my answer here: Wake Lock not working properly for links.
The problem is that context.startService(eventService) is an asynchronous operation which is very likely to return in just a few milliseconds. This means that when you acquire a WakeLock in your onReceive method, you keep it just for a couple of milliseconds and you release before the service starts.
A way to solve this is to share the a wakelock between your BroadcastReceiver and the service that you're trying to launch. This is how the WakefulIntentService works but you can also do this yourself, for example, by creating a singleton WakelockManager with two methods, one for acquiring and one for releasing a wakelock, then have you BroadcastReceiver call the former and your service call the latter.
Also, remember that leaking wakelocks (by acquiring one but forgetting to release it) can have serious consequences in terms of battery usage.
Related
Application is running SDK 23 and above. I am running some tasks using Service after completing the task and scheduling the next task using AlaramManager (setExactAndAllowWhileIdle). This is working fine. If the phone is idle continuously for 2 or 3 days then its going to Doze mode. After Doze mode ,application loosing the network and wakelock also not working.
Is there way even if phone is Doze can we run the application with any network interposition issues.I tried to keep the application witelist but i needs device to be rooted.
adb shell dumpsys deviceidle whitelist +<Package Name>
Can anyone suggest me which best way to run application without interruption?
Actually there is no way of doing this without running a foreground service. Having listed in white list may not be appropriate for your application and even though it is, you ask user to give you permission which can be seen as something dangerous from the end user's point of view.
However, I have a trick about this. Listen android's broadcasts and when you catch that device will move into doze mode, start a foreground service. In most of the cases user won't be able to see your foreground notification image and won't know that you are running a service. Because device is in the doze mode meaning it is stable in somewhere user not watching. So you can do whatever is needed.
You also listen broadcasts sent when doze mode is finished. When that happens, stop your foreground service and work in a normal logic of yours with alarm managers.
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()) {
startForegroundService();
//stopAlarmManagerLogic();
} else {
stopForegroundService();
//startAlarmManagerLogic();
return;
}
return;
}
}
Edit-WakefulBroadcastReceiver is now deprecated
Firstly, instead of directly calling a service in the AlarmManager call a broadcast receiver which then calls the service.
The broadcast receiver should extend a WakefulBroadcastReceiver instead of a regular BroadcastReceiver.
And then, let the broadcast receiver schedule a new Alarm, start the service using startWakefulService() instead of startService()
public class MyAwesomeReceiver extends WakefulBroadcastReceiver {
int interval=2*60*60*1000;
#Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context, MyAwesomeService.class);
Intent receiverIntent = new Intent(context, MyAwesomeReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 11, receiverIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+interval,alarmIntent);
startWakefulService(context, serviceIntent);
}
}
The WakefulBroadcastReceiver and startWakefulService() will let your app a 10 seconds window to let do what it needs to do.
Also,
You can always ask the user to let your app ignore battery optimization functionality using-
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
Intent intent=new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
if (powerManager.isIgnoringBatteryOptimizations(getPackageName())) {
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
}
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
and in the manifest
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"></uses-permission>
You can request android to whitelist your app for doze mode by sending a high pirority GCM message. But remember this might make your app not approved by Google Play:
Intent intent = new Intent();
String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm.isIgnoringBatteryOptimizations(packageName))
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
}
context.startActivity(intent);
https://developer.android.com/training/monitoring-device-state/doze-standby.html#whitelisting-cases
After Android N, no app can run in background forever. However you can use Firebase Job Dispatcher which can help you to run your app even if it is in doze mode. With the help of Firebase Job Dispatcher you can tell the system that your app should run at a particular time if provided conditions are matched.
I am a developer of two alarm clock apps on Google Play. I am trying to get them to work with Android 6.0. However, Doze mode makes it so they do not ring. I put them on the white list, I put a foreground notification icon up, I'm not sure what else I can do - when in Doze mode, the the Alarm Manager alarms are still ignored. The Clock app (which is a Google Play rather than AOSP app), however, is different. When the alarm is enabled on the Clock app, "adb deviceidle step" will always read "active" and never "idle", "idle_pending" or anything else.
Is Android cheating here, giving its own app more power, aka. "pulling an apple"? Are all alarm clock apps on Google Play about to become non-functional? Kind of worried here, these are quality apps that each took a year of part-time development time, and are big income sources for me. Any clues on how I could get these to work would be a huge help.
Setting the AlarmManager intent:
Intent intent = new Intent(context, ReceiverAlarm.class);
if (android.os.Build.VERSION.SDK_INT >= 16) {
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
}
amSender = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT); //FLAG_CANCEL_CURRENT seems to be required to prevent a bug where the intent doesn't fire after app reinstall in KitKat
am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, scheduleToTime+1, amSender);
and the ReceiverAlarm class:
public class ReceiverAlarm extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
if (wakeLock == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Theme.appTitle);
wakeLock.acquire();
}
X.alarmMaster.startRingingAlarm(true);
}
and the relevant parts of the X.alarmMaster.startRingingAlarm() method:
if (wakeLock == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Theme.appTitle);
wakeLock.acquire();
}
if (screenWakeLock == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
screenWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, Theme.appTitle+" scr");
screenWakeLock.acquire();
}
Intent alarmIntent = new Intent(Intent.ACTION_VIEW);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
alarmIntent.setClass(context, ActivityAlarmAlarm.class);
context.startActivity(alarmIntent);
Some of the methods have been pasted inline for easier readability.
Doze and App Standby definitely change the behavior in regards to alarms and wakelocks, but they're definitely not the end of the world for you!
Have you tried using the method setAlarmclock() instead of set()?
It's designed specifically for alarm clocks and may be able to cut through doze. There are a few adb commands you can use to manually put a phone into doze or app standby mode: https://developer.android.com/preview/features/power-mgmt.html
If that isn't able to wake your app up, there's the surefire method setExactAndAllowWhileIdle() is designed to wake the phone from doze no matter what. Worst case scenario, you can wake your app up with this method and use the wakeup to schedule the next alarm.
Another page worth a read is this blog post with its flowchart for background work and alarms: https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo
Put the application in whitelist only allows network in doze mode. AlarmManager does not affected by whitelist.
For setExactAndAllowWhileIdle() method please check the below description from SDK. It will not wake the phone from doze.
When the alarm is dispatched, the app will also be added to the
system's temporary whitelist for approximately 10 seconds to allow
that application to acquire further wake locks in which to complete
its work.
My intention is to make an application that will track the movement of my android phone for every few minutes and send it to my server. I have read a lot online on how to do it with a service, AlarmManager and Partial_WakeLock. I have also gone through the commonsware examples in github.com but I was a bit confused because I am still not experienced in android.
I have been successful in getting my application to [get location and send it to my server]. How do I make my service wakeup every few minutes and do [work mentioned]? In the Wakeful example in commonsware, in which method do I mention my [work] and in which method do I keep calling it?
You need a Service and an AlarmManager. Your Service will handle getting the position and posting it to the server and AlarmManager will invoke your service basen on an interval you decide. You should initialize your AlarmManager with your Service roughly like this in onCreate or other place you want:
AlarmManager alarmMgr = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, YourAlarmReceiver.class),PendingIntent.FLAG_CANCEL_CURRENT);
// Use inexact repeating which is easier on battery (system can phase events and not wake at exact times)
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, YOUR_ALARM_TRIGGER_AT_TIME,YOUR_ALARM_INTERVAL, pendingIntent);
YourAlarmReceiver gonna start your service
public class YourAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, YourService.class));
}
}
On how to use Services refer to the android website http://developer.android.com/guide/topics/fundamentals/services.html
you could use a partial wakeLock with a sleep(X) which when the sleep(x) is resolved the system will call the next line of code, but the problem is I am seeing a possible infinite cycle that might need a task kill action, or just crash the system.
I have a Service in my application which is designed to run every 10 minutes. It basically checks up on our servers to see if everything is running properly and notifies the user of any problems. I created this application for internal use at our company.
My co-worker used the application over the long weekend and noticed that no checks were performed when the device went to sleep. I was under the impression that the Service was supposed to keep running in the background until I explicitly call stopService() in my code.
So ultimately, my goal is to have the service running until the user hits the off button in the application or kills the process.
I heard about something called WakeLock which is meant to keep the screen from turning off, which is not what I want. I then heard of another thing called a partial WakeLock, which keeps the CPU running even when the device is asleep. The latter sounds closer to what I need.
How do I acquire this WakeLock and when should I release it and are there other ways around this?
Note: This post has been updated to include the JobScheduler API of the Android Lollipop release. The following is still a viable way, but can be considered deprecated if you're targeting Android Lollipop and beyond. See the second half for the JobScheduler alternative.
One way to do recurrent tasks is this:
Create a class AlarmReceiver
public class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Intent myService = new Intent(context, YourService.class);
context.startService(myService);
}
}
with YourService being your service ;-)
If you require a wake lock for your Task, it is advisable to extend from WakefulBroadcastReceiver. Don't forget to add the WAKE_LOCK permission in your Manifest in this case!
Create a Pending Intent
To start your recurrent polling, execute this code in your activity:
Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
//myAlarm.putExtra("project_id", project_id); //Put Extra if needed
PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Calendar updateTime = Calendar.getInstance();
//updateTime.setWhatever(0); //set time to start first occurence of alarm
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY, recurringAlarm); //you can modify the interval of course
This code sets up an alarm and a canceable pendingIntent. The alarmManager gets the job to repeat the recurringAlarm every day (third argument), but inexact so the CPU does wake up approximately after the interval but not exactly (It lets the OS choose the optimal time, which reduces battery drain). The first time the alarm (and thus the service) is started will be the time you choose to be updateTime.
last but not least: here is how to kill the recurring alarm
Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
//myAlarm.putExtra("project_id",project_id); //put the SAME extras
PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarms.cancel(recurringAlarm);
This code creates a copy of your (probably) existing alarm and tells the alarmManager to cancel all alarms of that kind.
of course there is also something to do in the Manifest:
include these two lines
< receiver android:name=".AlarmReceiver"></receiver>
< service android:name=".YourService"></service>
inside the < application>-tag. Without it, the system does not accept the start of recurrent alarm of a service.
Starting with the Android Lollipop release, there's a new way of solving this task elegantly.
This also makes it easier to only perform an action if certain criteria such as network state are met.
// wrap your stuff in a componentName
ComponentName mServiceComponent = new ComponentName(context, MyJobService.class);
// set up conditions for the job
JobInfo task = JobInfo.Builder(mJobId, mServiceComponent)
.setPeriodic(mIntervalMillis)
.setRequiresCharging(true) // default is "false"
.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED) // Parameter may be "ANY", "NONE" (=default) or "UNMETERED"
.build();
// inform the system of the job
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(task);
You may also provide a deadline with setOverrideDeadline(maxExecutionDelayMillis).
To get rid of such a task, just call jobScheduler.cancel(mJobId); or jobScheduler.cancelAll();.
I would have recommended, if building this application from the beginning to use a server-side component (yes, would also need monitoring!) and send push notifications, polling is never a reliable solution.
From Android Documentation in doze mode following happens: (https://developer.android.com/training/monitoring-device-state/doze-standby):
The system ignores wake locks.
The system does not allow JobScheduler to run.
Android ignores AlarmManager as well unless they are in setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
Network access is suspended.
So the only way is to use FCM on high priority or AlarmManager with setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
I have a service which sends notifications every 2 minutes..
When the phone gets locked no new notifications are received and only when I unlock my phone I receive notifications )
Whether there is any way to change it..?
Best solution for running some task every 2 minutes is AlarmManager And do not use wakelocks except you don't need to hold device in work mode (like mp3 player for example), because it will spend battery.
UPDATE FOR ENYONE WHO TO LAZY FOR READING MANUAL
For using AlarmManager you need broadcast receiver at first.
public class ExampleReceiver extends BroadcastReceiver
{
public void onReceive(Context context, Intent intent)
{
// this method will be called by AlarmManager. But be carefull it has timeout
// if your task need more time, you should run thread from there with wakelocks
}
}
Then you can set task to AlarmManager.
public class ExampleActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
Intent intent = new Intent(this,ExampleReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime()+INTERVAL,pendingIntent);
}
}
This example run task once after INTERVAL. And it will wakeup phone for this task. You can set repeating tasks due AlarmManager, or it possible better to set new task just from receiver
When phone is locked, the device goes to deep-sleep mode and it will not handle any wait/sleep timer related delays.
Try using AlarmManager instead for getting notification every 2 mins. Or may be hold WAKE LOCKS (though might result in battery drain and not recommended)
You might be able to accomplish your goal using the PowerManager API:
http://developer.android.com/reference/android/os/PowerManager.html
You can use this to wake the phone up briefly to handle your notification. Make sure you're familiar with how the PowerManager API works, though - it's easy to write your code in a way that drains the battery.