Run task periodically, *even when device is in idle* - android

I'm writing an app which should collect some sensor data every 10 seconds or so and write them to disk.
So far, I have an Activity which starts a service. The service has a runnable, which uses a handler to run my task periodically via handler.postDelayed(). See below for the (shortened) code.
So far, this works fine as long as the device is active. As soon as the device goes into idle, it doesn't run my task until it wakes up again.
So, my question is how to run my task ALWAYS.
With setExactAndAllowWhileIdle(), the AlarmManager seems to offer exactly what I need, but...
To reduce abuse, there are restrictions on how frequently these alarms will go off for a particular application. Under normal system operation, it will not dispatch these alarms more than about every minute (at which point every such pending alarm is dispatched); when in low-power idle modes this duration may be significantly longer, such as 15 minutes.
Battery life has just a minor priority, though not being awake the entire time would be fine. (Not sure if android can be awake for just a second or so)
MyActivity
...
public void onStartService(View view) {
Intent i= new Intent(getBaseContext(), MyAppService.class);
getBaseContext().startService(i);
}
public void onStopService(View view) {
stopService(new Intent(getBaseContext(), MyAppService.class));
}
....
MyService
public class MyAppService extends Service {
MyRunnable mr;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mr= new MyRunnable(getApplicationContext() );
mr.Start();
return Service.START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mr.Stop();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
MyRunnable
public class MyRunnable implements Runnable{
// context is needed for sensorListener (?)
private Context myContext;
private Handler handler;
public MyRunnable(Context c){
myContext=c;
handler= new Handler();
}
public void Start(){
run();
}
public void Stop(){
handler.removeCallbacks(this);
// some clean-up
}
#Override
public void run() {
//acquire and write to file some sensor data
handler.postDelayed(this, 10000);
}
}

i think what you are looking for is STICKY SERVICE.
Officail Docs: If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.
you just need to pass a flag at the time of calling the service.

Related

TimerTask stops firing in Service

I need to have a Service running in Android that stores a value to database every so often. How often is based on user preferences, and also if other events have happened, which can be as often as 30 seconds or up to 30 minutes.
This is not something that is hidden from the user and in fact the user should probably be aware its running. As such I think a foreground service is probably the best approach.
I have a foreground service running, with a TimerTask that calculates how often it needs to fire. That Service is 'sticky' so it should stick around and it low on resources the OS should start it back up after a while.
My problem is that the TimerTask seems to stop running after a while when the the app is backgrounded.
Here is my service:
public class TimerService extends Service {
private static final String LOG_NAME = TimerService.class.getName();
private Timer timer;
private final Handler timerHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
Notification notification = new NotificationCompat.Builder(this, "MY_APP_CHANNEL_ID")
.setContentTitle("My Timer Service")
.setContentText("Background timer task")
.setSmallIcon(R.drawable.timer)
.build();
startForeground(1, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
stopTimer();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void stopTimer() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
private void startTimer() {
stopTimer();
timer = new Timer();
long frequency = // calculate frequency
long delay = // calculate delay
timer.scheduleAtFixedRate(new MyTimerTask(), delay, frequency);
}
private void saveToDatabase() {
// Save some stuff to the database...
if (some condition) {
// might need to reschedule timer delay and frequency.
startTimer();
}
}
private class MyTimerTask extends TimerTask {
#Override
public void run() {
timerHandler.post(new Runnable() {
#Override
public void run() {
onTimerFire();
}
});
}
private void onTimerFire() {
try {
saveToDatabase();
} catch (Exception e) {
Log.e(LOG_NAME, "Error in onTimerFire", e);
}
}
}
}
Should this work? IE can I have a simple Timer in a foreground Service that fires continuously until that service is stopped? If so is there a bug in my code?
I chose a Timer to try to keep it simple, I only ever need one timer running and I wanted it to be able to reschedule easily. I do realize that I could try a Handler, ScheduledThreadPoolExecutor, or even an AlarmManager. I thought an AlarmManager might be overkill and a drain on resources if it is firing a ton. Not to mention rescheduling.
Why won’t it run in the background?
It is running in the background. It is not running when the device is asleep, as the CPU is powered down. In Android, "background" simply means "has no foreground UI" (activity, service with a foreground Notification).
I need to have a Service running in Android that stores a value to database every so often. How often is based on user preferences, and also if other events have happened, which can be as often as 30 seconds or up to 30 minutes.
What you want has not been practical on Android since 6.0.
I thought an AlarmManager might be overkill and a drain on resources if it is firing a ton.
That is true. However, the only way to get your existing code to work would be for you to acquire a partial WakeLock, thereby keeping the CPU running forever. This will be orders of magnitude worse for power than is AlarmManager. And AlarmManager is bad enough that each Android release, starting with 6.0, has made it progressively more difficult to use AlarmManager (or JobScheduler, or Timer and a wakelock) to do anything reliably.
You are going to need to learn what Android is and is not capable of with respect to background processing, then adjust your product plans accordingly. That subject is way too long for a Stack Overflow answer.
Here is a set of slides from a presentation that I delivered last year on this subject, with Android 8.0 in mind (use Space to advance to the next slide). You might also read:
My thoughts on Android P DP2, particularly "What’s New in the War on Background Processing"
This preview of one of my book chapters, where the section on Android 8.0 and its phase in "The War on Background Processing" happens to be included
Wakelock and doze mode
IMHO, writing an app that relies upon periodic background processing is a very risky venture nowadays.
You should use ScheduledExecutorService for the same. There can be many ways to schedule background task like Alarm Manager, JobDispatcher, Sync Adapter, Job Scheduler. I will suggest ScheduledExecutorService over them.
I have one good example of using ScheduledExecutorService in service. (Currently using in highly optimised location sync service)
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Created by KHEMRAJ on 1/29/2018.
*/
public class SyncService extends Service {
private Thread mThread;
ScheduledExecutorService worker;
private static final int SYNC_TIME = 60 * 1000; // 60 seconds
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
startSyncService();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
stopThread();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void stopThread() {
worker = null;
if (mThread != null && mThread.isAlive()) mThread.interrupt();
}
private void startSyncService() {
if (worker == null) worker = Executors.newSingleThreadScheduledExecutor();
if (mThread == null || !mThread.isAlive()) {
mThread = new Thread(new Runnable() {
#Override
public void run() {
saveToDb();
if (worker != null) {
worker.schedule(this, SYNC_TIME, TimeUnit.MILLISECONDS);
}
}
});
mThread.start();
}
}
private void saveToDb() {
// TODO: 5/15/2018
}
}

Android Service is terminated when App destroyed

I'm trying to learn Android Service, I'm very noob. I'm creating a service which will run even after the app is destroyed but when I terminate the App, the Service gets terminated too. I'm trying to make a NotificationService, below is my code that I just tried working with Service.
Manifest:
<service
android:name="com.test.testworks.MyService"
/>
Starting Service via Button Click:
startService(new Intent(this, MyService.class));
Service class MyService.class:
public class MyService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
/* 1. *//*ScheduledExecutorService scheduleTaskExecutor = Executors.newScheduledThreadPool(5);
// This schedule a runnable task every 2 minutes
scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
public void run() {
}
}, 0, 10000, TimeUnit.SECONDS);*/
/* 2. *//*final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
Toast.makeText(MyService.this, "suiubsibddubsuidv", Toast.LENGTH_LONG).show();
handler.postDelayed(this, 10000); //now is every 2 minutes
}
}, 10000);*/
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I'm checking on my phone where the other Services are running and when I terminate the App, the Service terminates also.
On some phones, you need to add your app explicitly to the list of apps that are allowed to run in the background. Otherwise, Android will not restart your app if it is killed for whatever reason. There should be a settings page which lists installed apps and allows you to add them to this list. It is called "protected apps" on some devices. Especially devices from Xiaomi, LG, Huawei have this feature, but also other phones.

Checking for new data in background

What is the recommended approach for checking for new data regardless if the app is in the foreground or background? I am wondering which Android API people are typically using to do this. There seems to be a few ways to achieve my goal, and I want to make sure I'm on the right path.
I have something put together which uses AlarmManager.SetInexactRepeating() to call an IntentService which does the sync and inserts/updates data in the database. This works while the app is in the foreground and background, but if I force stop the app then I keep seeing "Unfortunately, has stopped working" messages when the AlarmManager alarm would've triggered. In this case, I only care about checking for new data only when the app is running in the foreground or background.
My first thought is to detect when the app is force closed, and stop the alarm, but that does not seem possible. So I am asking here, is my approach wrong? If so, which approach is used to perform some periodic task regardless if the phone is in the foreground or background? The problem with the AlarmManager solution I am using is the alarms continue to fire even when the app is closed.
If your idea is to check if your API has new data and perform a background sync to your local database or other data storage, I think you would like to take a look at this:
Creating a Sync Adapter
Running a Sync Adapter
The Sync adapter is the recommended way of achieving this in Android. The pros of using it are multiple:
Optimisations out of the box - the OS bundles calls, uses the most appropriate windows to run the sync adapter at a minimal bandwidth and battery cost
The lifecycle of your background sync component is managed internally by the OS
Observers can be notified when data has been changed so the UI can be updated easily
Multiple ways of running the sync - at intervals, automatically with the OS message to keep TCP/IP connections open or on demand
However, implementing this requires some things, that can cause a bit of a pain at first:
It is mandatory that the adapter works with a ContentProvider
Sync Adapters use Account for authentication. If this is not needed, a Stub has to be provided
For backgrounding on Android usually you use even a Service that can run alone and independently from the App or a Bounded service that takes and returns data from the App. A complete reference on backgrounding can be found here
Using a Service is the right way to go. Have your app start the Service and it will continue running while the app is in the foreground or the background. Then, if you want to kill the Service when your app closes, you could just call stopService(yourServiceIntent); from the onDestroy() override in your app's activity. That should effectively shut down the service when the app closes.
So some sample code of how this works (taken from the Services docs)...
The Service (just Logs a message every 1 second for 60 seconds):
public class MyService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
long endTime = System.currentTimeMillis() + 60*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(1000);
Log.d("SERVICE", "The service is still running.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
}
}
And in your activity you would do something like:
public class MainActivity extends AppCompatActivity {
Intent serviceIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent(this, MyService.class);
startService(serviceIntent);
}
#Override
protected void onDestroy() {
stopService(serviceIntent);
super.onDestroy();
}

Start a Service from another Service and pass-by-value

I've got a Service in my Android application. During onStartCommand, I pass the Service object to another class. Then, from there, there's a thread that after 30 seconds starts another Service. It is something like this:
public class FooService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyClass mc = new MyClass(this);
mc.testMethod();
stopSelf();
return START_NOT_STICKY;
}
}
And this is MyClass:
public class MyClass {
private Service service;
public MyClass(Service service) {
this.service = service;
}
public void testMethod() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(20*1000);
Intent intent = new Intent(service, BarService.class);
service.startService(intent);
}
catch (Exception e) {
// CATCH!
}
}
}).start();
}
}
Now, as you can see, in FooService I call stopSelf() wich destroys that Service object. By the way MyClass has got a copy of that Service that was passed by value. After 20 seconds, we can start BarService from MyClass. I've tested it and it works but I can't understand why! The way I wrote the code is dirty (for me). Is it correct to start another service from one that was destroyed? Thank you for your help.
I've tested it and it works but I can't understand why
It works today on the environments you tested in. It may not work in all environments (e.g., ROM mods) and may not work tomorrow (e.g., Android OS updates). A destroyed Context, such as your stopped service, should not be used for anything. It happens that presently you can still use it to call startService() later, but that behavior is not guaranteed.
Is it correct to start another service from one that was destroyed?
No. In this case, I fail to see why you need two services in the first place.
I've got a copy of that service
No, you do not.

Android - Periodic Background Service - Advice

I am working on an app that will relay information about its location to a remote server. I am intending to do it by doing a simple HTTP post to the web-server and all is simple and fine.
But according to the spec, the app needs to execute itself from time to time, lets say once in every 30 mins. Be independent of the interface, meaning which it needs to run even if the app is closed.
I looked around and found out that Android Services is what needs to be used. What could I use to implement such a system. Will the service (or other mechanism) restart when the phone restarts?
Thanks in advance.
Create a Service to send your information to your server. Presumably, you've got that under control.
Your Service should be started by an alarm triggered by the AlarmManager, where you can specify an interval. Unless you have to report your data exactly every 30 minutes, you probably want the inexact alarm so you can save some battery life.
Finally, you can register your app to get the bootup broadcast by setting up a BroadcastReceiver like so:
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
// Register your reporting alarms here.
}
}
}
You'll need to add the following permission to your AndroidManifest.xml for that to work. Don't forget to register your alarms when you run the app normally, or they'll only be registered when the device boots up.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Here is a semi-different way to keep the service going forever. There is ways to kill it in code if you'd wish
Background Service:
package com.ex.ample;
import android.app.Service;
import android.content.*;
import android.os.*;
import android.widget.Toast;
public class BackgroundService extends Service {
public Context context = this;
public Handler handler = null;
public static Runnable runnable = null;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "Service created!", Toast.LENGTH_LONG).show();
handler = new Handler();
runnable = new Runnable() {
public void run() {
Toast.makeText(context, "Service is still running", Toast.LENGTH_LONG).show();
handler.postDelayed(runnable, 10000);
}
};
handler.postDelayed(runnable, 15000);
}
#Override
public void onDestroy() {
/* IF YOU WANT THIS SERVICE KILLED WITH THE APP THEN UNCOMMENT THE FOLLOWING LINE */
//handler.removeCallbacks(runnable);
Toast.makeText(this, "Service stopped", Toast.LENGTH_LONG).show();
}
#Override
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show();
}
}
Here is how you start it from your main activity or wherever you wish:
startService(new Intent(this, BackgroundService.class));
onDestroy() will get called when the application gets closed or killed but the runnable just starts it right back up.
I hope this helps someone out.
The reason why some people do this is because of corporate applications where in some instances the users/employees must not be able to stop certain things :)
http://i.imgur.com/1vCnYJW.png
EDIT
Since Android O (8.0) you have to use JobManager for scheduled tasks. There is a library called Android-Job by Evernote which will make periodic background work a breeze on all Android versions. I have also made a Xamarin Binding of this library.
Then all you need to do is the following:
In your application class:
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
JobManager.create(this).addJobCreator(new MyJobCreator());
}
}
Create the following two classes YourJobCreator and YourSyncJob(Where all the work will be done. Android allocates time for all the background jobs to be run. For android versions < 8.0 it will still run with an Alarm manager and background service as per normal)
public class MyJobCreator implements JobCreator {
#Override
#Nullable
public Job create(#NonNull String tag) {
switch (tag) {
case MySyncJob.TAG:
return new MySyncJob();
default:
return null;
}
}
}
public class MySyncJob extends Job {
public static final String TAG = "my_job_tag";
#Override
#NonNull
protected Result onRunJob(Params params) {
//
// run your job here
//
//
return Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(MySyncJob.TAG)
.setExecutionWindow(30_000L, 40_000L) //Every 30 seconds for 40 seconds
.build()
.schedule();
}
}
You should schedule your service with alarm manager, first create the pending intent of service:
Intent ii = new Intent(getApplicationContext(), MyService.class);
PendingIntent pii = PendingIntent.getService(getApplicationContext(), 2222, ii,
PendingIntent.FLAG_CANCEL_CURRENT);
Then schedule it using alarm manager:
//getting current time and add 5 seconds to it
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 5);
//registering our pending intent with alarmmanager
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), pi);
this will launch your service after 5 seconds of current time. You can make your alarm repeating.
You can use Alarm Manager to start Service at specified time and then repeat alarm in specified interval. When alarm goes on you can start service and connect to server and make what you want

Categories

Resources