Job Scheduler vs Background Service
Asked Answered
S

7

48

I have an app which has a feature A which should run in background every minute. Feature A is that the app should connect to a database, read some data then get the current location of the device and based on them check a condition, if the condition is true it should send a statusbar notification to the user so that when the user clicks on the notification the UI of the app will be displayed and something happens.
This background task should run permanently every minute, regardless the app is used, closed, terminated (like facebook or Whatsapp that show us notifications regardless they are in the app stack or not).
Now I have searched and have found that Android offers Job Scheduler,Background Service, AlarmManager and Handlers.
But the more I read about them the more contradictory the statements appear to me.

  1. About Handlers I have read that they do not exist for long delays and will be terminated after system reboot. So they won't be appropriate for my task.
  2. But AlarmManager seems to be a good candidate for the problem because when permitted they exist even after system reboot and can rerun the app. But in the Android Documentation that the Alarm Manager is intended to be used for tasks that have to be run at a specific time (like the Alarm Clock). But my task has to be run every minute.
  3. Then there is Background Service. This is more for tasks like downloading in the background as I have read and not intended for doing something I have explained.
  4. JobScheduler seems not to be for a task that has to be done in permanently, but for tasks that fulfill a specific constraint like idle, or no network... So which of these (or other ones if they exist) do you recommend to use for the task I explained in the first part
Spraddle answered 22/3, 2017 at 22:2 Comment(3)
You told that you need to fetch some data from a database and then check a condition. The best way to do it is pushing the information from the server.Karmenkarna
(...) The best way to do it is pushing the data from the server using GCM. But it requires a server improvement (maybe you can't make changes on the server). ( I know that it was not the question, but It maybe help who started in an wrong way looking for an app update.).Karmenkarna
@ErickM.Sprengel are server pushes reliably delivered? via say Firebase Messaging (previously GCM)Agency
N
36

I have an app which has a feature A which should run in background every minute.

That will not happen on hundreds of millions of Android devices, those running Android 6.0 and higher, due to Doze mode (and, possibly, app standby, depending on the rest of your app).

But AlarmManager seems to be a good candidate for the problem because when permitted they exist even after system reboot

No, they do not. You need to reschedule all alarms scheduled with AlarmManager after a reboot.

the Alarm Manager is intended to be used for tasks that have to be run at a specific time

AlarmManager supports repeating options.

This is more for tasks like downloading in the background as I have read and not intended for doing something I have explained.

A Service will be essential for whatever solution you wind up using.

JobScheduler seems not to be for a task that has to be done in permanently, but for tasks that fulfill a specific constraint like idle, or no network

JobScheduler, as with AlarmManager, supports repeating jobs.

So which of these (or other ones if they exist) do you recommend to use for the task I explained in the first part

Use none of them, as you cannot run things every minute on Android 6.0+ once the device goes into Doze mode, which will be within an hour of the screen turning off. Instead, either redesign the app to only need background work a few times per day, or do not bother writing the app.

Neighborhood answered 22/3, 2017 at 22:28 Comment(17)
So I can run the task every minute and it will stop when in doze mode. But after leaving the doze mode the task will run again, isn't it?Spraddle
@MoProg: Correct. However, devices are in Doze mode a lot of the time. That is particularly true on Android 7.0, where they added a "partial Doze mode" that kicks in immediately after the screen goes off.Neighborhood
But I have Android 7. So how I get always all this notifications although my screen is off for more than half an hour?Spraddle
@MoProg: If you are referring to Facebook and WhatsApp, they use Firebase Cloud Messaging, most likely. That is for a server pushing messages down to a device, for when data changes of relevance on the server. FCM messages are allowed to temporarily allow an app to do some work, even in Doze mode. If by "connect to a database", you mean that your app is polling some Web service, you could switch to a push model, using FCM, rather than trying to do something every minute on your device.Neighborhood
The last option to not bother writing the app is hyperbole. It's true Doze-mode stops background services from running while the device sleeps, but if your app absolutely requires it then you can still instruct your users and take them to the settings page to disable the "Battery Optimizations" setting for your app preventing bypassing Doze-mode from impacting your app's background services.Apollyon
@CordRehn: Note that Google had been banning apps that do what you describe ("take them to the settings page to disable the 'Battery Optimizations' setting for your app"). AFAIK, the closest that you can get safely is ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS, which takes them to the list.Neighborhood
@Neighborhood I am aware of that, as such I do the safe route as you've described. The user must search the list of apps for mine to manually disable the battery optimizations, slow and cumbersome but it's a one-time thing with huge benefits to the user in my case so I haven't heard any complaints.Apollyon
@Neighborhood Can I count on reliable delivery (across even reboots) for Firebase Cloud Messaging (FCM)? I. e. Does the reliability and instant-ness of Facebook & WhatsApp come automatically for free with FCM? Or, is there something additional that a developer needs to do? Thirdly, is it right to infer that FCM has a special status in Android+Google that other messaging providers can never have? (otherwise whatever method FCM uses could be used by an app+server combo and there would be a way to have an app wake up every minute by having its server message it every minute)Agency
@user2297550: "Does the reliability and instant-ness of Facebook & WhatsApp come automatically for free with FCM?" -- I would use FCM for opportunistic delivery, with periodic (unreliable) polling as a backup. "is it right to infer that FCM has a special status in Android+Google that other messaging providers can never have?" -- if by "Android+Google" you mean "Google Play ecosystem", then the big advantage that Google has is that their stuff is pre-installed, pre-added to the battery optimization whitelist, and can't be banned by Google.Neighborhood
@Neighborhood Thank you. Re "periodic (unreliable) polling" -- so back to square 1, what do Facebook & WhatsApp use for backup? Many of us want to write similarly responsive apps. Firebase is opportunistic, but we want both instant and reliable delivery of notifications similar to Messenger & WhatsApp. Last sentence of your answer says "few times a day" which is far from the reliability of Facebook/WhatsApp, "or don't bother writing the app" (appreciate the clarity) but the question is how do Facebook/WhatsApp get away with all these constraints?Agency
@user2297550: "so back to square 1, what do Facebook & WhatsApp use for backup?" -- you would have to ask them. I have no way to know. "which is far from the reliability of Facebook/WhatsApp" -- that is why I suggested to use it as a backup.Neighborhood
@tshm001: "Not only are you wrong and misleading" -- citation, please. "you intentionally deflect so as not to appear wrong" -- I have no idea what they might use for a "backup" solution, as asked in that comment. For their primary solution, they most likely use Firebase Cloud Messaging, as I mentioned. "Just for obvious examples: IFTTT, Waze and so on and so on" -- AFAIK, IFTTT uses Firebase Cloud Messaging and Waze hopefully uses a foreground service. "You scattered your answers in an unhelpful way for maximum effect" -- the comments are over a 15 month span.Neighborhood
Ok lets say IFTTT uses FCM. How is it doing the geolocation business logic with FCM alone? And if FCM alone can entirely handle that kind of bl, then wouldn't you say your answer was hyperbolic and misleading, and instead you should've instructed the OP to use FCM rather than to cancel their app and plan not to build it?Selinaselinda
How else do I know your answer was hyperbolic and wrong?I am currently working on a legacy app which was published multiple times to the play store with exactly the APIs and functionality you say is not allowed. And it has been published and accepted dozens of times since your answer.Selinaselinda
@tshm001: "How is it doing the geolocation business logic with FCM alone?" -- it wouldn't. "instead you should've instructed the OP to use FCM rather than to cancel their app and plan not to build it?" -- FCM is not suitable for doing something every minute. No solution is suitable for doing something every minute on modern versions of Android. "with exactly the APIs and functionality you say is not allowed" -- they are allowed. They just don't work the way developers expect, given Doze mode, app standby, and such, as I point out in the answer. They certainly don't work every minute.Neighborhood
IFTTT most definitely works every minute, 2 at a minimum. The moment I leave the geofence of my neighborhood it usually fires and turns off my EcoBee within a very small amount of time. It could be 1 to 2 minutes, but it's not nearly as impossible as you implied.Selinaselinda
@tshm001: Geofencing does not involve periodic work on the part of the app. That is handled by either the OS (LocationManager) or Play Services's fused location provider. Both of those are exempt from the sorts of background processing limitations that affect ordinary apps. Network restrictions on IFTTT might be lifted temporarily when it gets control from the geofence -- I haven't played with that specific scenario, and the docs are murky as usual.Neighborhood
S
15

You can use modern JobScheduler API which was introduced in Android 5.0 if your minSdkVersion=21.

Also there is https://github.com/firebase/firebase-jobdispatcher-android which requires installed Google Play minSdkVersion=9

But I recommend to use this library https://github.com/evernote/android-job where depending on the Android version either the JobScheduler, GcmNetworkManager or AlarmManager will be used.

With these APIs you can schedule your job and run service which describes task.

UPDATE Now it is better to use new WorkManager (docs). android-job will be deprecated soon

Sniffy answered 22/3, 2017 at 22:15 Comment(1)
Damn, they deprecate their APIs faster than they can update their docs. I am spending so much time running behind what the heck I should use now for some sort of background processing. Service? No! AlarmManager? No. AsyncTask? ThreadPool, Bound Service? BroadcastReceiver? JobScheduler? Firebase JobScheduler? WorkManager? FCM? Currently all the docs say use Firebase-JobScheduler. Now it is already WorkManager that might use Firebase or JobScheduler. Gosh.Patiencepatient
C
7

First, a JobService is a Service. A background service is ambiguous, let me guess you mean a service that runs in the background thread. Job Service runs on the ui thread but you can create an async task object within it to make it run in the background.

From your question, JobService is not the way to go.What i suggest is:

  1. You can create a class that extends IntentService (this runs on the background thread) in the onDestroy method of that class, send a broadcast and make the broadcast restart the service.

     @onDestroy(){
     Intent broadcastIntent = new 
     Intent("com.example.myapp.serviceRestarted");
     sendBroadcast(broadcastIntent);}
    
  2. Create a class that extends broadcast reciever

     public class RestartServiceReceiver extends BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {
     context.startService(new Intent(context, 
     MyService.class));
    } 
    }
    
    1. In your manifest, register your service and reciever
<receiver
            android:name=".RestartServiceReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.myapp.serviceRestarted" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

The boot permision is to enable the reciever be called the the system has finished booting, and once the reciever is called, the service will be called again.

Copulative answered 12/5, 2017 at 11:6 Comment(0)
D
3

Above Lollipop, i.e, API version 21, You can use a JobScheduler to schedule a JobService. To repeat a job every minute, you'll have to schedule the job everytime it is finished by setting the minimum latency to 60*1000 milliseconds.

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {

    boolean isWorking = false;
    boolean jobCancelled = false;

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d("_____TAG_____", "MyJobService started!");
        isWorking = true;

        doWork(params);

        return isWorking;
    }

    private void doWork(JobParameters params) {

        if (jobCancelled)
            return;

        //Create a new thread here and do your work in it. 
        //Remember, job service runs in main thread

        Log.d("_____TAG_____", "MyJobService finished!");
        isWorking = false;
        boolean needsReschedule = false;
        jobFinished(params, needsReschedule);

        scheduleRefresh();

    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d("_____TAG_____", "MyJobService cancelled before being completed.");
        jobCancelled = true;
        boolean needsReschedule = isWorking;
        jobFinished(params, needsReschedule);
        return needsReschedule;
    }

    private void scheduleRefresh() {

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            ComponentName componentName = new ComponentName(getApplicationContext(), MyJobService.class);
            JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
            builder.setMinimumLatency(60*1000);  //1 minute
            JobInfo jobInfo = builder.build();

            JobScheduler jobScheduler = (JobScheduler)getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
            int resultCode = jobScheduler.schedule(jobInfo);
            if (resultCode == JobScheduler.RESULT_SUCCESS) {
                Log.d("_____TAG_____", "MyJobService scheduled!");
            } else {
                Log.d("_____TAG_____", "MyJobService not scheduled");
            }
        }
    }

}

You can write a common function, anywhere you like, to schedule the job for the first time -

public void scheduleMyJobService() {

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        ComponentName componentName = new ComponentName(context, MyJobService.class);
        JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
        builder.setMinimumLatency(60*1000);
        JobInfo jobInfo = builder.build();

        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
        int resultCode = jobScheduler.schedule(jobInfo);
        if (resultCode == JobScheduler.RESULT_SUCCESS) {
            Log.d("_____TAG_____", "MyJobService scheduled!");
        } else {
            Log.d("_____TAG_____", "MyJobService not scheduled");
        }
    }
}
Dogwatch answered 28/9, 2019 at 15:6 Comment(2)
I request for a fair explanation before getting a down-vote, because it does exactly what the OP asked for: perform an action every minute, even when the app is killed.Dogwatch
you also need to register the service: <service android:name="com.example.appname.service.MyJobService" android:enabled="true" android:label="Ten Minutes Service" android:permission="android.permission.BIND_JOB_SERVICE" /> </application>Trollope
S
2

According to this and other link in comment 1 below

You should use AlarmManager for your task.

If you need to set alarms that fire while in Doze, use:

 setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

For a full easy to understand explanation for the different ways to do stuff in the background read: https://www.bignerdranch.com/blog/choosing-the-right-background-scheduler-in-android/

Good Luck!

Shelli answered 21/6, 2017 at 10:42 Comment(2)
Link 2 :developer.android.com/topic/performance/scheduling.htmlShelli
Recommend the BigNedRanch link above, it covers it very well. Basically use JobScheduler and forget about API < 21 which is 10% of market and falling. Though wondering why the article doesn't mention Service class ?Merthiolate
A
1

In the previous versions of Android, people used Handler or background services for this purpose. After a while, they announced alarm manager class for permanent, scheduled works.

Whatsapp, facebook or some social media applications mostly use google cloud messaging for the notification purpose which is not useful for you.

I will recommend you to use Alarm manager for this. After the KitKat version(4.2), Operating System blocks the background handler for better use of battery.

Background services are mostly used for image upload or some heavy process which has an ending time. When you are sending a video to your friend on Whatsapp, background process starts and uploads the video to backend server.

I am not sure about JobScheduler api for supporting the older versions of support, but it is as good as Alarm Manager.

Azilian answered 22/3, 2017 at 22:20 Comment(0)
D
0

you can do it by using service, with return start_sticky in "START_STICKY tells the OS to recreate the service after it has enough memory and call onStartCommand() again with a null intent. START_NOT_STICKY tells the OS to not bother recreating the service again. There is also a third code START_REDELIVER_INTENT that tells the OS to recreate the service and redeliver the same intent to onStartCommand()"

and set a TIMER with period 1 minute and do execute your code.

As well if you want to restart the service when the user force stop it, you can do that "as previous answers"

  1. You can create a class that extends IntentService (this runs on the background thread) in the onDestroy method of that class, send a broadcast and make the broadcast restart the service.

    @onDestroy(){
        Intent broadcastIntent = new Intent("com.example.myapp.serviceRestarted");
        sendBroadcast(broadcastIntent);
    }
    
  2. Create a class that extends broadcast receiver

    public class RestartServiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.startService(new Intent(context, MyService.class));
        } 
    }
    
  3. In your manifest, register your service and receiver

    <receiver
       android:name=".RestartServiceReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.example.myapp.serviceRestarted" />
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    

Also, you can use AlarmManager and If you need to set alarms that fire while in Doze, use:

setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

set it "current time in second + 60 sec" so you will set it next minute.

and execute your code and in the last, reset the AlarmManager next minute.

Also, you can start your service or AlarmManager after reboot the device just use a brodcastReciever when "RECEIVE_BOOT_COMPLETED"

and put this permission:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Destrier answered 16/10, 2017 at 23:34 Comment(2)
In higher android devices, above API 5 or 6, the device will enter doze state frequently if the user is not interacting with the app(and the app is in background) and the android system will kill the background service.Magenta
One point to @Code Pope is that according to the docs developer.android.com/training/monitoring-device-state/… the device goes into Doze mode when the screen is off, the device unplugged and - this is the interesting thing for your app - the device is stationary. According to this, although the device may go into Doze mode, it may not matter to you since the location will not have changed (and therefore the notification may not be needed). If on the other hand, the changes in the database alone are capable of triggering the notification, then this would not work.Culdesac

© 2022 - 2024 — McMap. All rights reserved.