I know there are many ways to call a function every x seconds or schedule a job. My question is what is the best fit solution for my problem?
I have a heavy task that I want to run in a background thread (Database queries, making HTTP calls).
For this I use an IntentService since it's onHandleIntent function runs in a background worker thread and the started services are queued.
The queue doesn't really matter for me since startService() will only be called from one place.
Now, I want to start this service frequently, sometehing like 10-15 seconds.
This was my old solution:
class MyService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
mutex = new Object();
while(isServiceRunning) {
doTheBigJob();
syncrhonized (mutex) {
mutex.wait(15000);
}
}
}
}
I assume this is bad practice.
Now my Intent service only contains doTheBigJob(). But how should I time my startService() call to make this happen every 15 sec for forever. What is the most optimal way?
This is a KIOSK app so it will always be in the foreground and the user can't escape it.
In one of my android applications, I need to run a task for every minute. It should run even if the app closes and when device is in Idle state also.
I have tried handler, it is working fine when device is active, but not working when device is in idle state.
I have tried workmanager(one time and repeated ) as well. Document says this works even when the device is in Idle mode, but this is stops working after 3/4 repeats.Workmanager is inconsitent, its working sometimes and not working most of the cases till i reboot device.
Can anyone suggest better way to handle the situation?
Thanks
bhuvana
Work manager can only work within 15 minutes of interval, if you do not define a longer time. To run something every minute, you need a Foreground Service with a sticky notification in it. There is no other way to run something every minute.
To start a foreground service, create a service as usual, and in its onStartCommand, call startForeground and from the method, return START_STICKY. These should achieve what you need.
Edit: Sample code for handler thread (this is Java btw, should be similar on Xamarin):
private HandlerThread handlerThread;
private Handler backgroundHandler;
#Override
public int onStartCommand (params){
// Start the foreground service immediately.
startForeground((int) System.currentTimeMillis(), getNotification());
handlerThread = new HandlerThread("MyLocationThread");
handlerThread.setDaemon(true);
handlerThread.start();
handler = new Handler(handlerThread.getLooper())
// Every other call is up to you. You can update the location,
// do whatever you want after this part.
// Sample code (which should call handler.postDelayed()
// in the function as well to create the repetitive task.)
handler.postDelayed(() => myFuncToUpdateLocation(), 60000);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
handlerThread.quit();
}
My Requirement is
Android application has to send user location details(latitude & longitude) to the server for every one hour(which is configurable).
The approach I followed is using the alarm manager i am invoking my service at configured intervals which will send the location details to server irrespective of whether the application is running.
Is this a good approach?
I prefer ScheduledExecutorService, because it is easier for background Tasks.
AlarmManager:
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.
ScheduledThreadPoolExecutor:
You can use java.util.Timer or ScheduledThreadPoolExecutor (preferred) to schedule an action to occur at regular intervals on a background thread.
You can see complete answer here => Which is Better ScheduledExecutorService or AlarmManager in android? And Here
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
// Hit WebService
}
}, 0, 1, TimeUnit.HOURS);
Yes, using AlarmManager is a good approach
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.
please refer this https://developer.android.com/training/scheduling/alarms.html
Android service run on UI thread so you should not execute long running task in it, like sending data to server. The approach you can use is ScheduledThreadPoolExecutor or AlarmManager for scheduling and using asynctask or any other background thread for sending data to servers
I prefer Timer for repeated tasks.
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
process();
}
};
Timer mTimer = new Timer();
mTimer.schedule(timerTask, 0,60*60*60*1000);
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.
I am implementing a Service that starts when Android boots, and it's supposed to scan every 10 mins for nearby Bluetooth devices. When it discovers devices, it does some work. Also, this periodic scanning should occur the entire time the device is on. I am trying to schedule a TimerTask, but I don't understand how to use it. I guess it should start this service and let the service do the work instead of writing the code in the TimerTask's run method? How would I start the service from the TimerTask as this seems the easiest way to remedy my problem, but TimerTask is part of java.util and not one of Android's classes.
I just found Android's AlarmManager. Should I use that? Can it start a Service?
So far I have this, but I need help:
class Timer extends Service
{
private Handler myHander;
Runnable r = new Runnable()
{
run()
{
startService(new Intent(Timer.this.getApplicationContext() ,MyService.class));
myHandler.postDelayed(r,10 minutes);
}
}
onCreate()
{
myHandler=new MyHandler();
}
onStartCommand()
{
//Do the bluetooth work.
r.run();
}
onDestroy()
{
super.onDestroy();
myHandler.removeCallback(r);
}
}
class MyService extends Service
{
}
Sorry, I don't understand how the formatting works here.
Will I need to override onDestroy() in the Service? Where to do I use stopService() ?
You need to:
Write service that will be started from one of your activities and/or after device has booted.
In your service in onStart/onStartCommand you need to schedule either using Handler or AlaramManager periodic updates.
The difference between Handler and AlarmManager in this case will be that: Handler will not run when device is in deep sleep, while you can configure AlaramManager to wake up the device and run your code.
I'd recommend to go with Handler, as its easier and because you said you only need to run your code when device is awake.
And one more thing, Handler doesn't use extra thread while TimerTask does. And this is considered a bad practice on Android to waste on thread for timer only.
An example code for how to repeat task using Handler can be found here: Repeat a task with a time delay?.