Running a long running service - android

I have written a service that fetches data from a CRM based web service and puts in a database on the phone. Now this service has to be run every 3 hours, so it can sync data between CRM and the android database.
Now to have this service run itself, I'm using alarm manager and have the web service "start" itself.
Intent intent = new Intent(ServiceClass.this, ServiceClass.class);
PendingIntent pintent = PendingIntent.getService(ServiceClass.this, 0, intent, 0);
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 10800*1000, pintent);
This service needs to be started after a reboot, for that purpose I'm using the method outlined here..
I just want to know if I'm going on the right path, or if I'm making a mistake or if there is a better way to do this. I haven't worked with Android much and just need a few pointers. Thanks!

Yes you are on the right path. The Alarm Manager is very reliable and is used specifically for this purpose - to schedule tasks in the future (both repeating and non-repeating).
And I agree with #tyczj, you should definitely define your ServiceClass as an IntentService. An IntentService is a subclass of Service which runs in the background and is specifically designed to perform a specific task, and then kill itself once it's finished. It's perfect for downloading data and background syncing.
It's quite easy to implement as well, you'll most likely just need to override one method - onHandleIntent - which is what is called when the service starts.
To re-schedule the tasks on reboot, the method outlined in that post is what I use. Hope this helps!

Related

Using AlarmManager to sync data with a server

First of all, I apologize for my English.
I need to sent data to a server and query for data in that server from time to time, maybe from week to week.
So I'm going to use the AlarmManager to do that. I wanna know if I can register two separated pending intents in the alarm manager, one to push data to the server, and another one to query data from the server. Can I register those two intents every time the application executes? Or do I need to test if I already got those intents registered?
I'm asking that because I'm afraid that I register one alarm to execute one week from now and then the user close the app and start again and the alarm is registered again one week from now and in the end will never be launched.
Another thing, is it safe to register a service to be executed with the AlarmManager?
Oh and this is the code I'm using:
long WEEK_IN_MILLIS = AlarmManager.INTERVAL_DAY * 7;
PendingIntent querySyncService = PendingIntent.getService(context, 0, new Intent(context,
QuerySyncService.class), PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pushSyncService = PendingIntent.getService(context, 1, new Intent(context,
PushSyncService.class), PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC, WEEK_IN_MILLIS, WEEK_IN_MILLIS, querySyncService);
alarmManager.setInexactRepeating(AlarmManager.RTC, WEEK_IN_MILLIS, WEEK_IN_MILLIS, pushSyncService);
I was thinking in putting this code inside some method and then just call the method every time the app start.
And I also saw that the solution I should probably use is the SyncAdapter, but the SyncAdapter just seems to be dirt, with a lot of code that does nothing, for example, you need one Stub Authenticator, then one Stub Authenticator Service and one Stub Content Provider and again one Stub Content Provider Service, then a lot of code for the Sync Adapter, then another bunch of code to only run the SyncAdapter. This just seems too dirty for me since I don't need a content provider neither one Authenticator.
You can, most certainly, register two separate Intents, with the AlarmManager. Just be sure to use different ids in your call to PendingIntent.getService... (and, btw, I suggest that you not use 0 as an id. 0s are just to easy to come by, in Java). Use the ACTION, or a parameter in the Extras, to distinguish between them.
There is no reason to use a BroadcastReceiver: you can fire the intent at the Service directly. If you are concerned about security, don't export the Service. A PendingIntent is fired from within your application, so there is no need to make the Service externally visible.
Finally, although you are right about the overhead for a SyncAdapter, it really is a great way to do exactly what you are trying to do. There is a good description of how to write one in EnterpriseAndroid (I am one of the authors). Once you get through the boiler-plate, it is actually almost elegant.
There is an example of a minimal SyncAdapter here.

Android background Services, Alarms and preserving object reference after application restart

I'm developing a little Android app, that needs to run a background process, used to start a remote connection periodically (for example, to check if there is new data on the server). This process obviously needs to work also if the application activity is not running at the moment.
As I can see in documentation, there are two types of approach to develop a scheduled background process in Android, working also when the application is closed.
Services
Alarms
The first one is not so good for my requirementes, because it can be killed by OS in case of low memory, so it is useless for me. startForeground() is not so good because I want the process is silent.
Alarm is ok, because it can't be killed by the OS, so it can work indefinitely. But... If I schedule an Intent with the AlarmManager, how can I preserve a reference to the Intent, surviving at application restart?
For example, if I want to cancel, or reschedule the Alarm, I need the reference to the initial Intent to cancel it thorugh the "AlarmManager.cancel(Intent i)" method. But if the application was restarted by the user, how can I obtain a reference to the initial Intent that was used to start the alarm?
Is there another way to stop an alarm if the launching application was restarted?
about alarms, you can cancel using the intent characteristics, so you don't need a reference to the original intent. In any case, the alarms mechanism still need you to run something on a service.
In any case, you missed another possible solution: SyncAdapter. Its purpose is to sync with servers, but you can do whatever you wish in the code, and it's unlikely the OS will kill it, as opposed to the other solutions you've mentioned.
Sadly even now it lacks on documentation and samples, but I think it can fulfill your needs. Here's what I've found
yes, note that cancel is looking for a PendingIntent, not an Intent per se.
so
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, R.string.some_string, new Intent(this, InitialIntent.class), 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
gets you where you want to be.
note that I'm not getString'ing that string, that's becasue I want a unique Id/request code for this intent that I'm not going to screw up copypasting; I reference the same number when creating the alarm in InititalIntent.
This question was just answered, here:
How can I get the context of other activity?
Just cancel the intent you scheduled
Your best bet would be to make use to push notification (via GCM), whenever something new is available on the server. I am working on similar application where data can be pushed from one side (producers) and needs to be pushed to the consumer apps.
You need to look at BroadcastReceiver and GCM specific communication model. Hope this helps

Performing a Scheduled task from a Service and Keeping that service Alive

public class MyHibernatedService extends Service{
public void onCreate() {
super.onCreate();
//declaring the intents ..
//some codes...
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, IntentServiceToBeRun, 0);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MILLISECOND, 10000);
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
}
I want my Service to launch an IntentService every single day at a certain time. However, I've read somewhere on the internet, StackOverflow, google,etc.. that a Service cannot stay alive forever, this is how Android works. It will have to kill that Service whenever Android needs memory or is using too much memory.
What guarantees for me that this code will run everyday and my Service will not be killed?
EDIT :
I noticed in the Android docs:
The Android system will attempt to keep the process hosting a service around as long as the service has been started or has clients bound to it
what do they actually mean by "clients bound to it"?
Don't use your own service to periodically start another service, but use the AlarmManager service instead (like you are already doing). There is no need for this second service IMHO.
The AlarmManager is a system service and will never be killed by Android, and it already does exactly what your own service would do: start things periodically (or single-shot at a specified time).
On the "clients bound to it": off-topic, but it means other components (like other services, or activities) are connected to it using a call to Context.bindService(). For details, check the dev guide.
You don't actually need to keep your service alive.
Here you are setting the AlarmManager, that is able to wake your IntentService up when it is time, even if the phone is sleeping. This service does the job, not yours.
What guarantees for me that this code will run everyday and my Service
will not be killed?
The code that you provided in MyHibernatedService has to be executed only once, to set the alarm, then you won't need it anymore. If it is the only thing that MyHibernatedService does, then you don't need it at all.
You could for instance create a static method in your intent service that would set the alarm, and call it from anywhere according to what you want (probably the place where you used to start MyHibernatedService).
Also, if you don't really need an incredible accuracy on the time your intent service is started, prefer setInexactRepeating() instead of setRepeating().

Best moment/location for setting an AlarmManager

My application shows content for a site that also has a notification system. I want to show if there are new notifications, and I am using an AlarmManager that calls an IntentService.
My question is: where should I start/register this AlarmManager? I've put it in the onCreate() of my activity just for proof-of-concept (and its working fine, thank you very much :) ), but if you would start that activity twice, you would get multiple alarms.
The only possible solution I've come up with is this, but I don't know if this would be best practice
Start the manager in an onCreate() if the preference "alarm started" is false
Set some variable that it is started in preferences.
Now if the alarm stops for some reason, there's no way to restart it. So, a variation would be:
Always call cancel in the onCreate()
And then always set the Alarm.
This seems like a common pattern: Wanting to periodically get information with an alarm, and not setting that alarm more then once. How should I do this? When, where and how to register the alarm?
Also, continueing on #Zelimir 's comment: can you check if a certain alarm is allready set?
Ideally, the alarm would be set regardless of the activity being started or not of course, but that might be another thing.
For completeness, this is the code I'm currently using to start the alarm:
AlarmManager alMan = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, CommentService.class);
PendingIntent penInt = PendingIntent.getService(this, 0, i, 0);
alMan.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES,
penInt);
For even more completeness, the app description / situation.
The app is basically showing blogs (journals if you will) from a certain page. It has activities for adding entry, viewing entries, adding comments, etc. On the 'mother' site there is an option to recieve notifications (like the number you see here on SO too when you get a message). I want to show if there are new messages, and so retrieve them every xx minutes. It would be shown in the notificationbar for now, but it might feed some sort of widget later.
If you need more info: the app is called Androblip and it supports a site called blipfoto.com
When, where and how to register the alarm?
That is impossible to answer in the abstract. It depends entirely upon what the business rules are for your app, which you declined to supply in your question.
If the monitoring is to be happening all the time, a typical pattern is to register the alarm:
in onCreate() of your main activity for the very first run of your app
in a BOOT_COMPLETED BroadcastReceiver, to handle reboots, which wipe the AlarmManager roster
can you check if a certain alarm is allready set?
No, but you can cancel it without issue. Just create an equivalent PendingIntent and call cancel() on the AlarmManager.

How do I keep running a Service the whole time?

I have some process that I would like to run in the background the whole time.
So I made it a Service.
My question is: is there anyway to prevent from the user from killing this service? (even if he uses third party app)
No, it is not possible. It is also a bad idea.
Maybe this can help you:
final Intent intent = new Intent(context, YourService.class);
final PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
final AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pending);
long interval = 30000;//milliseconds
alarm.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(),interval, pending);
This way your service will be receiving regular onStart events from AlarmManager. If your service is stopped it will be restarted. So you may say it will be running infinitely.
More complete sample can be found in Photostream sample application http://code.google.com/p/apps-for-android/.
•A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.)
Send a broadcast in the onDestory method of the service, restart the service in the onReceive method the the broadcast receiver.
But make sure the resources your service are under control
See: http://developer.android.com/reference/android/app/Service.html#ProcessLifecycle

Categories

Resources