Polling via an IntentService and using AlarmManager - android

I am just having a hard time understanding the AlarmManager system, tied in with future notifications etc. I can't seem to get the AlarmManager to set alarms at the correct frequency.
I'm also unsure where I should actually create an alarm, and what type is most suited to my needs.
Updates are based on Preferences which will be detailed below.
Launching the Service
When my application is launched, the service is called when a FragmentActivity is created. Within the onCreate(), I have the following:
Intent mServiceIntent = new Intent(this, ServiceUpdater.class);
if(startService(mServiceIntent)==null)
startService(mServiceIntent);
Lots of other stuff happens and fragments are shown ect, but this is essentially to ensure the service is running. Note, I also have it set to call the BroadcastReceiver on System startup.
ServiceUpdater.class
public class ServiceUpdater extends IntentService{
private SharedPreferences defaultPrefs;
private SharedPreferences userPrefs;
private SharedPreferences.Editor editor;
// Alarm Service
private AlarmManager alarmManager;
private PendingIntent alarmIntent;
public ServiceUpdater() {
super("MyService");
}
#Override
protected void onHandleIntent(Intent intent) {
defaultPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
userPrefs = getSharedPreferences("user", Context.MODE_PRIVATE);
long updateFrequency = (Long.parseLong(defaultPrefs.getString("updateFrequencyPref", "24")))*1000*60*60;
// in hours = 1000*60*60*'24'
long thirtySecondsAfterWake = SystemClock.elapsedRealtime() + 30*1000; // 30 seconds after the device boots
Intent intent1 = new Intent(this, UpdateReceiver.class);
alarmIntent = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, thirtySecondsAfterWake, updateFrequency, alarmIntent);
if(defaultPrefs.getBoolean("updatePref", true))
new Update().execute();
}
private class Update extends AsyncTask<Void, Void, Integer>{
private int newNumber;
#Override
protected Integer doInBackground(Void... params) {
newNumber= new HttpServices().getNewNumber();
return newNumber;
}
#Override
protected void onPostExecute(Integer noJobs){
if(newNumber > 0){ //userPrefs.getInt("oldNumber", 0)){
editor = userPrefs.edit();
editor.putInt("oldNumber", newNumber);
editor.commit();
if(!FragActivity.active)
new MyNotif(getApplicationContext(), "NewData");
}
}
} // end Async
} // end service class
UpdateReceiver.class
public class UpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent updater = new Intent(context, ServiceUpdater.class);
context.startService(updater);
}
}
---------------------
My guess is that when the alarm calls the Receiver, it creates a new Alarm, and then updates the old one, setting the next update time to be 30 seconds in the future; which explains why the HTTP request is happening (and I'm getting notifications every 30 seconds or so.
I want to ensure my alarm runs even when the application is not running.
My aim for this application is to do the following.
Run the App / or / On Phone Startup
Start a service that polls (e.g. Daily) a single Http Request
Regardless of the response, ensure the Polling happens again the following iteration timeframe
A notification will fire from onPostExecute based on the results of the polling
Where do I correctly set the Alarm so that it is not recreated every time the service is - but runs correctly even when the application isn't running?
Thanks for any help in advance.

but this is essentially to ensure the service is running
I have no idea why you are starting the service twice.
ServiceUpdater.class
I have no idea why you are using an AsyncTask inside an IntentService. This is bad on several levels. onHandleIntent() is already called on a background thread, and I would expect your app to flat-out crash when trying to create an AsyncTask on a background thread. Just move all the background logic into onHandleIntent() (or methods called by onHandleIntent()).
My guess is that when the alarm calls the Receiver, it creates a new Alarm, and then updates the old one, setting the next update time to be 30 seconds in the future; which explains why the HTTP request is happening (and I'm getting notifications every 30 seconds or so.
I have no idea why you are setting up a repeating alarm on every run.
Where do I correctly set the Alarm so that it is not recreated every time the service is - but runs correctly even when the application isn't running?
Your code to set up the AlarmManager schedule needs to be executed:
On the first run of your app (since you have not had an opportunity to set up the alarms yet)
After the user force-stops your app from Settings (since your previously-scheduled alarms are wiped out)
After a reboot (since your previously-scheduled alarms are wiped out)
A typical pattern for handling the first two is to keep track of when your alarm code last ran, such as in a SharedPreference. Then, either in a custom Application or in a likely entry point of your app (e.g., launcher activity), check the SharedPreference value for your last alarm run time:
If there is no value, that means it's the first run of your app, so you schedule the alarms
If there is a value, and it is significantly longer than your polling period, you assume that your app was force-stopped, and so you schedule the alarms
Otherwise, there is a plausible value, so you assume that your alarms are working just fine
Handling the reboot scenario is a matter of running through your AlarmManager scheduling logic in response to an ACTION_BOOT_COMPLETED broadcast.
Note that you are using ELAPSED_REALTIME, which means that the alarm will be delayed if the device is in sleep mode.
Also note that you are using setInexactRepeating(), which means that the alarm will go off sometime within the polling period, but not at a precise interval.

Related

Starting new Alarm from service started by AlarmManager

In my app i am using AlarmManagerto start service every week on a specific time. But in some cases instead of waiting another 7 days I need to call the service on the next day. Because of that I'm not using reapeating alarm but instead in the started service I'm creating new Alarm and set it to a specific date. Something like this:
public class AlarmService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//do something
//setting new alarm
AlarmManager alarmMng = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this,AlarmService.class);
PendingIntent alarmIntent = PendingIntent.getService(this, 0, i, 0);
Calendar c = Calendar.getInstance();
if(something) alarmMng.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis()+1000*60*60*24,alarmIntent);
else alarmMng.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis()+1000*60*60*24*7,alarmIntent);
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
Everything works fine but I wonder if this is good way to do it. Is it a good idea to create new alarm from service that was just called by one? Is it considered bad programming practice? If so how else should I handle it?
Is it considered bad programming practice?
No - this is a fine use case for creating alarm events. If you look at the documentation, use of AlarmManager is intended to send events to your app even if it is not running. Receiving those events in a Service that then schedules another alarm event is perfectly fine. The rest of my answer is intended to explain how to answer the other question you ask:
Is it a good idea to create new alarm from service that was just
called by one?
To determine if you need a Service really depends on the "do something" portion of your code more than setting the alarm. For example, you might be fine using a IntentService or even a BroadcastReceiver.
EDIT:
In other words, you will need a background process to handle this. Determining the appropriate background process (Receiver or Service) depends on how much processing needs to be done. Generally, setting an alarm all by itself could probably be handled in a Receiver but if it takes too long to process (e.g. more than 10 seconds) you will get an ANR (Application Not Responding) crash. That's when you need a service.
END EDIT..
This is a good post about services: Service vs IntentService
Specifically, the concern you should have is if your service is called multiple times, you should probably include code to cancel any previous alarms created by it, before setting a new alarm.
EDIT: Also, you are not "creating a new service" or "new alarm" each time. Services will have onStartCommand called each time an intent is sent to it (by the AlarmManager or by any other means). A new instance is not created unless it is not already instantiated.

Only want Alarm to die when removed from Recent Apps or user forced killed

I have an application that does something in a service triggered by an alarm every 10 minutes while the app is in the background. I want the Alarm to keep going off even if the OS kills the app (So its dead and have to relaunch) and only kill it if the user actually removes the app from recent apps or force kills it from settings (or is logged off but that already works). I have tried many different approaches, latest I tried this http://www.dotkam.com/2011/01/10/android-prefer-alarms-and-intent-receivers-to-services/ without any luck. My Alarm looks like this.
if(ActivityLifecycleHandler.isApplicationInForeground()) {
return; // If App is in foreground do not start alarm!
}
String alarm = Context.ALARM_SERVICE;
AlarmManager am = ( AlarmManager ) context.getSystemService( alarm );
Intent intent = new Intent(locationBroadcastAction);
PendingIntent pi = PendingIntent.getBroadcast( context.getApplicationContext(), 0, intent, 0 );
int type = AlarmManager.ELAPSED_REALTIME_WAKEUP;
// Set trigger time to 0, because want to fire off the first one instantly
am.setRepeating( type, 0, ONE_MINUTE, pi );
And the BroadcastReceiver:
public class LocationBroadcastReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent ) {
Intent myIntent = new Intent( context, LocationService.class );
context.startService( myIntent );
}
}
Any idea of what I am doing wrong? Everything works fine until the OS kills the app.
The alarm should goes off if the app was killed, even if if was removed from the recent app (but not if it was force-stopped).
It is actually the whole point of using the AlarmManager :
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.
You can see if the alarm is still there with :
adb shell dumpsys alarm
What is probably happening is that the device is woken up to receive the alarm, but fall asleep again as soon as the receiver finishes, before the service can start.
The receiver should take a wake lock (to be released by the service). The support library provides a WakefulBroadcastReceiver which does exactly that :
Helper for the common pattern of implementing a BroadcastReceiver that
receives a device wakeup event and then passes the work off to a
Service, while ensuring that the device does not go back to sleep
during the transition.

Background Service getting killed in android

We have developed an Android Application which involves a service in the background. To implement this background service we have used IntentService. We want the application to poll the server every 60 seconds. So in the IntentService, the server is polled in a while loop. At the end of the while loop we have used Thread.sleep(60000) so that the next iteration starts only after 60 seconds. But in the Logcat, I see that sometimes it takes the application more than 5 minutes to wake up (come out of that sleep and start the next iteration). It is never 1 minute as we want it to be.
What is the reason for this? Should background Services be implemented in a different way?
Problem2
Android kills this background process (intent service) after sometime. Can't exactly say when. But sometimes its hours and sometimes days before the background service gets killed. I would appreciate it if you would tell me the reason for this. Because Services are not meant to be killed. They are meant to run in background as long as we want it to.
Code :
#Override
protected void onHandleIntent(Intent intent) {
boolean temp=true;
while(temp==true) {
try {
//connect to the server
//get the data and store it in the sqlite data base
}
catch(Exception e) {
Log.v("Exception", "in while loop : "+e.toString());
}
//Sleep for 60 seconds
Log.v("Sleeping", "Sleeping");
Thread.sleep(60000);
Log.v("Woke up", "Woke up");
//After this a value is extracted from a table
final Cursor cur=db.query("run_in_bg", null, null, null, null, null, null);
cur.moveToLast();
String present_value=cur.getString(0);
if(present_value==null) {
//Do nothing, let the while loop continue
}
else if( present_value.equals("false") || present_value.equals("False") ) {
//break out of the while loop
db.close();
temp=false;
Log.v("run_in_bg", "false");
Log.v("run_in_bg", "exiting while loop");
break;
}
}
}
But whenever the service is killed, it happens when the the process is asleep. The last log reads - Sleeping : Sleeping. Why does the service gets killed?
The main problem is that we cannot say
Services are not meant to be killed. They are meant to run in background as long as we want it to.
Basically, that is not true. System still can terminate the service in low memory and possibly other situations.
There are 2 ways to overcome this:
If you are implementing the service, override onStartCommand() and return START_STICKY as the result. It will tell the system that even if it will want to kill your service due to low memory, it should re-create it as soon as memory will be back to normal.
If you are not sure 1st approach will work - you'll have to use AlarmManager http://developer.android.com/reference/android/app/AlarmManager.html . That is a system service, which will execute actions when you'll tell, for example periodically. That will ensure that if your service will be terminated, or even the whole process will die(for example with force close) - it will be 100% restarted by AlarmManager.
You could use ScheduledExecutorService designed specifically for such purpose.
Don't use Timers, as demonstrated in "Java Concurrency in Practice" they can be very inaccurate.
IntentService is not intended to keep running in a while loop. The idea is to react to an Intent, do some processing and stop the service once done.
That does not mean that it's not working and I can't tell you why you see such long delays but the cleaner solution is to use some external source to poke the service periodically. Besides vanilla Java methods you can also have a look at the AlarmManager or a Handler as mentioned in the AlarmManager documentation.
The Handler way would work like this
public class TriggerActivity extends Activity implements Handler.Callback {
// repeat task every 60 seconds
private static final long REPEAT_TIME = 60 * 1000;
// define a message id
private static final int MSG_REPEAT = 42;
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(this);
}
#Override
protected void onStart() {
super.onStart();
// start cycle immediately
mHandler.sendEmptyMessage(MSG_REPEAT);
}
#Override
protected void onStop() {
super.onStop();
// stop cycle
mHandler.removeMessages(MSG_REPEAT);
}
#Override
protected void onDestroy() {
super.onDestroy();
mHandler = null;
}
#Override
public boolean handleMessage(Message msg) {
// enqueue next cycle
mHandler.sendEmptyMessageDelayed(MSG_REPEAT, REPEAT_TIME);
// then trigger something
triggerAction();
return true;
}
private void triggerAction() {
// trigger the service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.setAction("com.test.intent.OPTIONAL_ACTION");
startService(serviceIntent);
}
}
A simple Activity (which could be extended to have that functionality in all your activities) that sends itself a Message all the time while it is running (here between onStart and onStop)
A better solution would be have an AlarmManager go off every 60 seconds. This AlarmManager then starts the service that polls the server, the service then starts a new AlarmManager, its a recursive solution that works quite well.
This solution will be more reliable as you dont have the threat of the Android OS killing your service, looming over you. As per API: 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.
In your UI/main activity etc, set this timer, to go off in 60 seconds:
long ct = System.currentTimeMillis(); //get current time
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); //60 seconds is 60000 milliseconds
In yourservice.class you could have this, it checks the connection state, if its good it sets the timer to go off in another 60 seconds:
public class yourservice extends IntentService {
public yourservice() { //needs this constructor
super("server checker");
}
#Override
protected void onHandleIntent(Intent intent) {
WifiManager wificheck = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if(check for a certain condition your app needs etc){
//could check connection state here and stop if needed etc
stopSelf(); //stop service
}
else{ //poll the server again in 60 seconds
long ct = System.currentTimeMillis();
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi);
stopSelf(); //stop service since its no longer needed and new alarm is set
}
}
}
Services get killed. Like app gets killed. It is Android philosophy that you can get killed at any time.
You should as other wrote not make the assumption that your backgroundservice runs forever.
But you can use a foreground service to drastically reduce the chance of getting killed/restarted. Note that this forces a notification which is always visible. For example music players, vpn applications and sportstracker use this API.
For Problem 1, from vanilla Java, Thread.Sleep() is guaranted to wake the thread after the timer has expired, but not exactly after it has expired, it may be later depending mainly of the statuses of other threads, priority, etc.; so if you sleep your thread one second, then it will sleep at least a second, but it may be 10 depending of a lot of factors, i'm not very versed in Android development, but i'm pretty sure it's the same situation.
For Problem 2, services can be killed when memory get low or manually by the user, so as others have pointed probably using AlarmManager to restart your service after a certain time will help you to have it running all the time.
Sound like you should be using a Service instead of an IntentService but if you want to use an IntentService and have it run every 60 seconds you should use the AlarmManager instead of just telling the Thread to sleep.. IntentServices want to stop, let it and have AlarmManager wake it up when it should run again.
Android is pretty good about killing off long running services. I have found CommonsWare's WakefulIntentService useful in my application: https://github.com/commonsguy/cwac-wakeful
It allows you to specify a time interval as you are trying to do by sleeping.
It could be probably for two reasons..
Either the while loop is creating an issue, it is making the handler to work until temp==true
Adding to it is threads, that is creating long delays upto 6 seconds.
In case, the system is working for a large database, creating long delays between each query will add on the system memory.
When the memory usage for application become so huge that the system memory gets low, system has to terminate the process..
Solution for the Problem..
You could replace above with Alarm Manager to revoke system services after a particular interval of time using Alarm Manager.
Also for getting intent back after the system recovers the application from termination, you should use START_REDELIVER_INTENT. It is to get your last working intent back after the application terminates. for its usage, study https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT
You can try Jobscheduler implementation with JobService running in Background, which is recommended above Android O.

Android - service keep alive

have a problem with a system. I have running service, which is constantly checking the position and counting the distance and time since user start it. But after 20-25 minutes and many interactions with other applications service is being killed.
How I can prevent it?
I'm thinking to add second service which will keep my alive.
Not sure if this will work for you, but this is how i implemented it:
In my case I needed a service to keep running in the background every X minutes, and whenever it is shutdown (whether due to memory usage or main activity going to background and Android cleaning it up) it would be re-triggered again when the next time interval is reached. I had the following components and workflow:
Activity A. Main activity, the starting point of my application.
Service S. Service which I want to run in the background, do whatever it needs to do,
shutdown after completion, and start again after every X minutes.
Activity onCreate method would create a PendingIntent, passing it itself and the service S, as follows:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create an IntentSender that will launch our service, to be scheduled
// with the alarm manager.
periodicIntentSender = PendingIntent.getService(
ActivityX.this, 0, new Intent(ActivityX.this, ServiceS.class), 0);
In my activity, I have an AlarmManager implemented which will take the "periodicIntentSender" (defined above) as an argument and based on user preferences (connection_Interval) sends the intent:
// Schedule the alarm to start
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, connection_Interval, periodicIntentSender);
AlarmManager will make sure that the intent will be sent every X minutes.
My Service S keeps listening to this Intent and gets wakedup each time such an Intent is sent. As soon as the service is triggered again, its onHandleIntent method gets called.
public class ServiceS extends IntentService implements LocationListener {
.
.
/*
* (non-Javadoc)
*
* #see android.app.IntentService#onHandleIntent(android.content.Intent)
*/
#Override
protected void onHandleIntent(Intent intent) {
<WHATEVER YOU NEED TO DO>
}
}
Hope this helps.
1, minimize your service's memory usage
2, make you service foreground, for example in the service's onCreate method
#Override
public void onCreate()
{
super.onCreate();
Notification notification = new Notification(R.drawable.icon_app_small, getText(R.string.app_name),System.currentTimeMillis());
Intent notificationIntent = new Intent(this, [yourService].class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, [name string], [notification msg string], pendingIntent);
startForeground(Notification.FLAG_ONGOING_EVENT, notification);
}
But after 20-25 minutes and many interactions with other applications
service is being killed.
The most likely it's caused by too much memory usage and then automatic memory manager killed your process or long running operation as meant #AljoshaBre.
How I can prevent it?
So my first idea is to check if your Service is running in some life-cycle method for example in onResume() and if not, just you should restart your Service and execute it again.

Android AlarmManager and BroadcastReceiver running in background Service

I'm currently having a problem integrating AlarmManager and BroadcastReceiver.
Im my app, I'm running a background service that runs regardless that app is running or not. I think I get this part working fine. The background service keeps an array that changes based on user's location. However, at 8:00am everyday, I want the app to reset the array variable to default. After much looking around online, it seems like the way to do this is via AlarmManager (to initiate the task every 8:00am) and using BroadcastReceiver (to receive the alarm, and perform the task).
So basically the code goes something like this:
public class BackgroundService extends Service {
private ArrayList thisArray;
private BroadcastReceiver thisReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
setArrayToDefault();
}
}
#Override
public void onCreate(){
super.onCreate();
Calendar cal = new GregorianCalendar();
cal.add(Calendar.MINUTE, 2); //example
this.registerReceiver(thisReceiver, new IntentFilter("BackgroundService"));
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), ONE_MINUTE, "what to put here?"); //example, repeat every minute
}
private void setArrayToDefault(){
//here, the array will be changed back to default values
}
}
My main issue is on how to set the AlarmManager to call thisReceiver everytime it's set. Any idea? Is my approach correct?
Im my app, I'm running a background service that runs regardless that app is running or not.
Please don't. This is why users attack us with task killers and the Force Stop from the Manage Services screen in Settings.
My main issue is on how to set the AlarmManager to call thisReceiver everytime it's set. Any idea?
You are not registering the BroadcastReceiver, so AlarmManager will not be able to contact it.
Please please please please please please please redesign your service such that it does not have to run all of the time. Android will be killing off your service due to old age, anyway, so if you want a reliable and stable app, you need to do this redesign, anyway.

Categories

Resources