I have a service that needs to compare previous and current html code.
I have configured it to do its work every 1 hour, but it actually does it in weird unlinear intervals. I have added to the code a command to write a log file in order to see whenever the work is done. The result is weird: sometimes the interval is less than an hour, sometimes much more (2-3 hours), and sometimes really 1 hour... edit: when the interval is shorter (1 minute) it operates normally here's my code:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(new Runnable() {
public void run() {
try {
if (isNetworkAvailable()) {
doServiceWork();
check("SLeEPING!!!", c);
}
else {
check ("NO INTERNET", c);
}
}
catch (Exception e) {check("78", c);}
}
}, 1, 3600, TimeUnit.SECONDS);
}
Use AlarmManager. You are making the thread sleep which I am not sure Android guarantees it will work that way. Something like this should help you. This will start the service every hour:
PendingIntent serviceIntent= PendingIntent.getService(context,
0, new Intent(context, MyService.class), 0);
long firstTime = SystemClock.elapsedRealtime();
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
long currenciesIntervalInSec = 3600;
if (automaticCurrencies)
am.setRepeating(AlarmManager.ELAPSED_REALTIME, firstTime, currenciesIntervalInSec*1000, serviceIntent);
Edit:
You should extend IntentService. It takes care of the threads and everything is done in the background (Its particulary useful if you need to do things in the background and you don't need several threads at the same time). Whatever you put in HandleIntent will be executed when you start the service. Internally it will keep a list of the queue.
For example this is a simple service class(You NEED to create both constructors):
public class MyService extends IntentService {
public MyService() {
super("MyService");
}
public MyService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent arg0) {
if (isNetworkAvailable()) {
doServiceWork();
check("SLeEPING!!!", c);
}
else {
check ("NO INTERNET", c);
}
}
}
Also, you need to add this in your manifest (Of course with your own package):
<service android:name="com.services.MyService"/>
Maybe you activity is destroyed by the Android task Manager and it is not shutdown by the timer. This is why you are getting shorter values. And this is why, when setting a very low value, you don't get that.
You should read about how Android handles the Processes and applications here.
Related
I've implemented a background service to update data in my app periodically.
The mechanism works well if my android device is turned on but causing issues when Android is in sleep mode:
Let's imagine the service is running every 15 minutes, then Android goes to sleep for 1 hour and when weaking up again, the service will be executed 4 times at once.
The prefered bahaviour would be running the service once only, in case it missed 1 or more cycles due to sleep.
To run my code periodically, I'm using TimerTask:
public class updateService extends IntentService {
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
if(mTimer != null) {
mTimer.cancel();
}
mTimer = new Timer();
int timer = getPreference("refresh_interval") * 1000;
mTimer.scheduleAtFixedRate(new updateTask (), timer, timer);
return super.onStartCommand(intent, flags, startId);
}
class updateTask extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
// Do job
}
});
}
}
}
I'd appreciate any suggestions how to do better. Thanks!
You are using Timer.scheduleAtFixedRate(TimerTask task, long delay, long period) which explains in its documentation:
If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to "catch up."
It seems you want to use Timer.schedule(TimerTask task, long delay, long period) instead, which says:
If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well.
If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well. He is right
I'd like to run a service in background but after I read the docs I'm a bit confused.
(I'm targeting SdkVersion 27, which means I can't start a BroadcastReceiver from the AndroidManifest.xml and need to do these tasks within the Application class, afaik.)
Before I was starting the IntentService within a BroadcastReceiver, which I started by using a PendingIntent, which was triggered by an AlarmManager. It felt a bit too much, so I started the service directly on the onCreate() of the Application.
It's working, but I'm not sure if that's a good practice.
The service is supposed to run forever and fire it's own threads for operations that can take up to one minute and run again as soon as they are finished (+ 5 seconds).
Pseudocode of the services purpose
MyService // starts on Application creation an runs "forever"
threads = []
itemIds = []
async loop manageThreads // start / kill
itemIds = getItemIdsFromDatabase()
loop itemIds vs threads
if noThreadRunningForCurrentItemId
threads.push(new ItemThread(itemId).start())
loop threads vs itemIds
if threadRunsForNoneExistingItemId
threads[currentItemId].kill()
sleep(20000) // manage threads every 20 seconds
ItemThread(int itemId)
doSomething()
sleep(5000)
ItemThread(itemId) // restart thread every 5 seconds
I'd like to avoid that the service get's killed by Android, blocks other Threads or leads to memory leaks.
What's the best practise for this use-case, any idea?
I don't think my question is opinion-based, because I guess that a pattern exists, which I'm not aware of yet.
App.java
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
// Start MyService to run in the background
Intent service = new Intent(this, MyService.class);
this.startService(service);
}
}
MyService.java
public class MyService extends IntentService {
public MyService() {
super("MyService")
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
try {
int i = 0;
while(true) {
Log.d("MyService", "i = " + String.valueOf(i));
i++;
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
I want to make a app which starts a foreground service to check which are other app are running at any time.
started services and running some logic part in different thread. Created notification and a timer (loop ) which check running apps at every 100 millisecond.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
new Thread() {
public void run() {
startInForeground();
}
}.start();
return START_STICKY;
}
private void startInForeground() {
createNotification();
startTimer();
}
startTimer() function is below, which is checking any app is running in background. Everything is fine, i am able to detect which app is running.
public void startTimer() {
ScheduledExecutorService scheduler = Executors
.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
checkAnyAppOpen();
}
}, 0, 100, TimeUnit.MILLISECONDS);
}
My concern is- I have to run checkAnyAppOpen() function again and again for infinite period of time. So is it good ? or is there any other way to handle it.
What is impact of this method?.
My second question is- I am able to stop my service but still timer(scheduler) is running continuously. How can i stop it ?. How can it affect if it not stopped as i have to again restart my service after some period of time which may run 5 hour to 24 hour. I have to again do stop and restart.
Thanks
I believe for your use-case it is applicable. Just make sure there are no leaks and the service is closed properly.
Use scheduler.shutdown() to stop executor.
I've been looking around some anwers given here but i don't get exactly the solution to my problem: I don't want to create a new class and extends runnable or thread.
I have a service that when created must check every 10 secs some stuff and the calls needed can't be done from the main thread, so onStartCommand() method I do the following:
mThread = new Thread() {
public void run() {
while(true) {
// some code
try {
Thread.sleep(10000);
}
catch (Exception e){
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
Log.i("Exception", errors.toString());
}
}
Now, when i call onStopService() i want to stop this thread. The method stop() is deprecated so I'm using interrupt():
#Override
public void onDestroy(){
mDelete.interrupt();
mDownload.interrupt();
super.onDestroy();
}
As I expected, it throws InterruptedException because the thread is sleeping when calling interrupt.
Is there any way to stop the thread without creating a new class and extend from runnable or thread?
Thanks in advance
As you can see here, you can use this code inside your thread:
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// We've been interrupted, and returning from the run method will stop your thread.
return;
}
However, in general it is better avoid infinite loop service. You should consider to stop your service when the work is done, and restart it (using an AlarmManager) when a new work is required. I will not repeat the code for use IntentService and AlarmManager, since Eugen Pechanec has given you a good explanation.
1) Extend your service from IntentService.
This type of service is used for short granular operations and it runs on a background thread so you can access network. Do your work in method onHandleIntent(Intent) instead of onStartCommand(Intent, int, int).
2) Schedule this service using AlarmManager (the following code will work in an activity).
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, Service.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
long nowElapsed = SystemClock.elapsedRealtime();
long tenMinutes = 10 * 60 * 1000;
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, nowElapsed, tenMinutes, pi);
Notice the word "inexact". That means that the interval won't be exactly 600000 milliseconds. It is more energy effective.
The PendingIntent.FLAG_CANCEL_CURRENT flag is for proper rescheduling.
3) Cancel the PendingIntent when you don't need it anymore. After this your service will not run automatically until you enable it again.
Intent i = new Intent(this, Service.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_NO_CREATE);
if (pi != null) pi.cancel;
More about using alarms: https://developer.android.com/training/scheduling/alarms.html
More about IntentService: http://developer.android.com/reference/android/app/IntentService.html
I have a scenario where I need to run a certain task at specific interval, however, I want to be able to reset/restart the timer without reinstantiating. Here's my code for better understanding.
private TimerTask beatTask = new TimerTask() {
#Override
public void run() {
beatDetected();
}
};
public void beatDetected() {
timeoutTimer.cancel();
// handle my stuff and restart the timer.
timeoutTimer.schedule(beatTask, 2000);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
(timeoutTimer = new Timer()).schedule(beatTask, 2000);
return Service.START_STICKY;
}
The idea behind this implementation is that beatDetected() can be called from outside event, in which case the next timer tick should happen from that moment on i.e. the elapsed time for the next task should be reset. However, I only get the first tick, and from that point on, the timer just doesn't work.
I'm not limited to using Timer class, anything that will solve the above scenario will work. I was thinging about using postDelayed, but this code is located in a Service, and I don't really need UI thread aware updates.
Using separate thread for timer is considered a somewhat bad practice on Android devices. That's because you are going to waste resources in most scenarios.
If you don't need super precise timing events, you should go with Handler based timers. An example of such timer can be seen here: Repeat a task with a time delay?. This approach works both for Activities and Services.
Also keep in mind that both Handler and Timer based timers will be paused if device goes to sleep mode. If this is not what you need, then use AlarmManager (but keep in mind that using AlarmManager incorrectly may lead to very bad battery performance).
Reseting the Handler based timer:
void resetRepeatingTask() {
mHandler.removeCallbacks(mBeatDetector);
mHandler.postDelayed(mBeatDetector, mInterval);
}
The accepted answer does not work (at least not for me).
A simple solution uses a Timer to trigger a TimerTask, which can easily be reset.
Log.d(TAG, "Restarting timer");
if (timer != null) {
timer.cancel();
}
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Log.d(TAG, "Run the task");
}
}, 1000);
Note that a new TimerTask must be created every time you reset the timer - you can't reuse the old one. If you want to preserve state you will need to get the timer task to run a separate task.