I would like to build an app that can track how long a user spends on certain apps (mainly for proof of concept and to implement in other apps.) I know that Lollipop implemented the API UsageStatsManager that enables you to track the "total length of time an app was in the foreground for a time interval (by day, week, month, or year)."
I have also read that one can poll the AcvtivtyManager continuously, however that would waste battery life and take up CPU and RAM.
What I am wonder is, is there an efficient way to keep track of the amount of time an app is open on devices running an android version lower than Lollipop. Specifically how apps like Aptrax and App Usage Tracker as well as many others, do it with very little or "No battery drain (even on high frequency tracking)."
Is there another method that these apps use, to get app usage data on pre-Lollipop devices, with little battery usage, or is polling the Activity manger not as resource heavy as they source I read lead me to believe.
i have tried to do the same but found no better way, basicklly poll the activity manager, now to make sure not to waste battary use the alarmManager and a recieverto set the waking times of the pollin so that when the phone is asleep it wont be woken for your poll (assume that user uses apps when the screen is on and phone is active) and poll like every 5 seconds or so
here is basic sample code
Intent alarmIntent = new Intent(context, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, Calendar.getInstance().getTimeInMillis(), pendingIntent);
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
// poll activity manager and any thing else you want here
// including figuring out the next time you want to run
// and scheduling another PendingIntent with the AlarmManager
}
}
using AlarmManager.RTC makes sure the reciever is only polling when the phone is awake.
Related
The requirement is to record the position every 90 seconds and write it to database(sqlite) and if network is available then call an api, even if the app is closed.
Which will be the best possible way to achieve this?
Should I start an intent service? Or I should go with an alarm Manager. I have read somewhere that alarm manager should only be used if the duration is more than 10 minutes.
I suggest to use AlarmManager since it can be run even Device is asleep.
Note that, for API v > 19, the alarm delivery is inexact and you have to use setExact api to achieve that .
See
Note: Beginning in API 19, the trigger time passed to this method is treated as inexact: the alarm will not be delivered before this time, but may be deferred and delivered some time later. The OS will use this policy in order to "batch" alarms together across the entire system, minimizing the number of times the device needs to "wake up" and minimizing battery use. In general, alarms scheduled in the near future will not be deferred as long as alarms scheduled far in the future.
With the new batching policy, delivery ordering guarantees are not as strong as they were previously. If the application sets multiple alarms, it is possible that these alarms' actual delivery ordering may not match the order of their requested delivery times. If your application has strong ordering requirements there are other APIs that you can use to get the necessary behavior; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent).
Alarm Manager would be the ideal choice for your situation. Registered alarms are retained while the device is asleep, but will be cleared if it is turned off and rebooted.
So, you can use this to run your job in 90 seconds interval. It may break the interval only first time after the device is rebooted.
Another way I found out was using the Firebase job dispatcher. That is the best option for devices running on marshmallow or further. It is easy to integrate and customizable.
https://github.com/firebase/firebase-jobdispatcher-android
A Service is an application component that can perform long-running
operations in the background and does not provide a user interface.
https://developer.android.com/guide/components/services.html
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.
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.
https://developer.android.com/reference/android/app/AlarmManager.html
Go with the service.
You go with Intent service and Broadcastreceiver componenst, because those components only listen continuously, even if you close the Application.
And you want to get something evry 90 seconds, so use Alarm Manager.
AlarmManager manager = (AlarmManager) (context)
.getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(context, YourAlarmReceuver.class);
//alarmIntent.putExtra("syncData", favoritesArrayList);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), 90*1000, pendingIntent);
Your Receiver class Like this
public class SyncAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent stIntent = new Intent(context,
YourService.class);
context.startService(stIntent);
}
And Your Intent Service Class like this
public class DataSyncService extends IntentService {
public DataSyncService() {
super(DataSyncService.class.getName());
// TODO Auto-generated constructor stub
}
#Override
protected void onHandleIntent(Intent intent) {
//write your logic here
// get the location and update the database
}
}
You should instantiate the alarm manager boot completed also.
* 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 made a program that creates a Service.
The service connects to a website every 5 minutes, collects one line of data and outputs it to a file.
My problem is that it didn't collect the data every 5 minutes. The data was collected once in around 50-70min. Why? Is there something in android that prevents background services from running properly during nights? What is also strange to me, the 50-70 minute breaks happened only when I was not using my phone.
Times it collected the data:
1:40
1:45
1:50
1:55
2:00
2:05
2:10
2:15
2:20
2:25
2:30 I was awake until this.
3:20
4:15
5:00
6:05
6:55
7:55
8:45
10:20
10:50 Woke up at here.
10:55
Your results are not surprising. Android was designed to swipe out (and back in) processes based overall level of resource as well as other factors.
Modifying your service to become a foreground service will certainly improve your results:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
Foreground services are basically services guaranteed by Android to be less susceptible to swipe out,
which is what you want.
But even a foreground service will halt its processing when the devices goes to sleep. And, if you feel your polling logic is important enough so to keep the device from sleeping, you should also acquire a wakelock.
Note: Long running wakelocks have a huge impact of on battery usage. Handle with care!
Finally, please consider using an alarm for your task. Alarms are usually considered better option for
long running polling tasks.
Depending of an approach you use to schedule your updates, the OS might shift updates to decrease battery drain and overall burden. Delayed tasks might be postponed if the device falls into deep sleep (happens at night mostly as the device is inactive during long periods of time).
It's done this way to prevent your battery to be drained to fast. If you really want to wake your device every 5 minutes, you can check the answer to this post and use the AlarmManager
I need to do a background check in my app, basically polling various URLs for new stuff. So I've read this is done with the AlarmManager.
The thing is, I don't need the user to be notified as soon as new stuff arrives. Let's say I'm checking every hour, I'm perfectly fine with not doing any check while the phone is sleeping, and resuming the checks when the phone is used. That way the user eventually gets the notification, but the drain is minimal.
So I've tried with setInExactRepeating. I'm starting it at boot time, but also at app startup (mostly for the session after install, and to make sure it's running even if the app gets "force killed"). How do I prevent all these checks to add up? (A new check should only be run if none is running, or the previous one needs to be stopped).
I've found setInexact Repeating coupled with ELAPSED_REALTIME (or RTC) works quite well to achieve very minimal battery drain. Using the flag FLAG_UPDATE_CURRENT on an existing pendingintent (same intent and id) will simply refresh the current alarm schedule if it already exists. Alternatively, add the code suggested by Ralgha to check and see if you already created the pending intent and set the alarm, and then if not, set the pending intent and alarm schedule. Both methods will likely have the same impact on battery (negligible). What will cause significant drain is doing something on a frequent schedule. The fact that you only want to update every hour and not when the screen is off, your app will use barely any battery (assuming it is done with it's processing relatively quickly and cleans up after itself nicely)
http://developer.android.com/reference/android/app/AlarmManager.html#ELAPSED_REALTIME
ELAPSED_REALTIME
This alarm does not wake the device up; if it goes off while the device is asleep, it will not be delivered until the next time the device wakes up.
http://developer.android.com/reference/android/app/AlarmManager.html#setInexactRepeating%28int,%20long,%20long,%20android.app.PendingIntent%29
setInexactRepeating
These alarms are more power-efficient than the strict recurrences supplied by setRepeating(int, long, long, PendingIntent), since the system can adjust alarms' phase to cause them to fire simultaneously, avoiding waking the device from sleep more than necessary.
http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_UPDATE_CURRENT
FLAG_UPDATE_CURRENT
...if the described PendingIntent already exists, then keep it but its replace its extra data with what is in this new Intent. This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.
You can check to see if the alarm is already set before you set it, and you can also use a flag so the alarm won't be delivered until the device is awake.
if ( PendingIntent.getBroadcast( context, 0, updateIntent, PendingIntent.FLAG_NO_CREATE ) == null )
{
pendingIntent = PendingIntent.getBroadcast( context, 0, updateIntent, 0 );
getSystemService( ALARM_SERVICE ).setInexactRepeating( AlarmManager.RTC, System.currentTimeMillis(), AlarmManager.INTERVAL_HOUR, pendingIntent );
}
I must build an application with a GPS tracker running during about a day. I'm aware of similar questions in SO but I haven't found any answers to some questions I have.
-I need a GPS fix every 10 min, so I think the best way to do it is to start the location service, get a fix (or timeout) and stop the service (with removeUpdates()). How can I have an application (or service or whatever) running this cycle every 10min and be sure it will continue as long as there is some battery left (even if device goes to sleep, it should wake it up every 10min to get a fix)? Is using AlarmManager a good idea?
-Can I expect the battery to last one day with this method?
I've checked mytracks but the gps listener seems always on and the battery is expected to last no more than 5h.
I've also checked CWAC Location Poller but it does only removeUpdates() on timeout and restart the listener immediately. It also uses a wakelock while in my case I think an AlarmManager could be a better idea.
Any help/suggestion welcome
You are spot on with alarm manager I use
Intent serviceIntent = new Intent(this, TrackerService.class);
mPendingIntent = PendingIntent.getService(this, 0, serviceIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(mPendingIntent);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), interval, mPendingIntent);
in a similar app for getting network location
interval is ms between starting the service
the service starts up, gets the location and closes
this was MUCH more battery efficient that hanging around with an active service waiting for reports
that code i posted cancels the previous alarms first so you don't get more than 1 :)
You can check out Passive Receiver
It'll give you an location update whenever another the devices location gets updated - only works on 2.2 or later