问题
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.
- 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.
- 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.
- 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.
- 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
回答1:
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.
回答2:
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
回答3:
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:
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);}
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)); } }
- 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.
回答4:
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!
回答5:
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.
回答6:
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"
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); }
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)); } }
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" />
回答7:
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");
}
}
}
来源:https://stackoverflow.com/questions/42963494/job-scheduler-vs-background-service