Why do we need the new Android WorkManager if we already have a JobScheduler along with a few nifty backports (AndroidJob and FirebaseJobDispatcher) with the same functionality? Does it have any killer-features or something? Because I don't see anything that makes me want to migrate to the yet another scheduler.
问题:
回答1:
"WorkManager has a lot of nice features but its main goal is to use the JobScheduler's API on older devices"... Wait, but we already have some backports. What's wrong with them? To cut it short:
FireaseJobDispatcher is fine but it requires Google Play to schedule jobs which isn't good if we're targeting China, for example.
Evernote's AndroidJob is an excellent backport with a lot of functionality. Imho, it was the best choice for scheduling any work. But now the latest version of the library uses the aforementioned WorkManager under the hood. And, unfortunately, sooner or later the library will be deprecated:
If you start a new project, you should be using WorkManager instead of this library. You should also start migrating your code from this library to WorkManager. At some point in the future this library will be deprecated.
They suggest to switch to the WorkManager because it provides more features and they also give us a short comparison:
| Feature | android-job | WorkManager |
| ------------------ | ----------- | ----------- |
| Exact jobs | Yes | No |
| Transient jobs | Yes | No |
| Daily jobs | Yes | No |
| Custom Logger | Yes | No |
| Observe job status | No | Yes |
| Chained jobs | No | Yes |
| Work sequences | No | Yes |
Imo, the the last 3 features are very useful and supported only by the WorkManager. So the answer to my last question is yes, it does have some killer-features:
- No Google Play required
- Queryable
- Chainable
- Opportunistic
To learn more about WorkManager one should definitely watch this talk by Sumir Kataria
P.S. If anyone knows why FirebaseJobDispatcher is actively supported by Google engineers instead of being deprecated write in the comments below :)
回答2:
WorkManager just seems like Google's answer to Evernote's Android-Job library, but with a few improvements. It uses JobScheduler, Firebase JobDispatcher and AlarmManager just like Android-Job depending on the device's API level. Their usage of tags looks pretty much the same and assigning constraints to jobs/work are similar enough.
The two features that I am excited about are: being able to chain work and the ability to be opportunistic on work with constraints. The first will allow work (jobs) to be broken up and more modular for me. And with more modular work, each piece of work may have fewer constraints improving the chance they will complete earlier (opportunistic). For example, the majority of processing work can complete before the work with a network constraint needs to be met.
So if you're happy with your current scheduler implementation and the two features I mentioned don't add value, I don't see a huge benefit to making the switch just yet. But if you're writing something new, it'll probably be worth it to use WorkManager.
回答3:
WorkManager
uses JobScheduler
service to schedule the jobs. If JobScheduler
is not supported by the device, then it uses Firebase JobDispatcher
service.
If Firebase JobDispatcher
is not available on the device, it will use AlarmManager
and BroadcastReceiver
.
So with WorkManager
, you don't need to worry about backward compatibility. In addition to this, it allows for defining constraints, which need to be met in order for the job to be run, such as defining network constraints, battery level, charging state and storage level.
It allows task chaining and passing of argument to the job.
http://www.zoftino.com/scheduling-tasks-with-workmanager-in-android
回答4:
WorkManager sits on top of JobScheduler and AlarmManager. WorkManager picks the right APIs to use, based on conditions like the user's device API level
WorkManager is a simple, but incredibly flexible library that has many additional benefits. These include:
-Support for both asynchronous one-off and periodic tasks.
-Support for constraints such as network conditions, storage space, and charging -status
-Chaining of complex work requests, including running work in parallel.
-Output from one work request used as input for the next.
-Handles API level compatibility back to API level 14.
-Works with or without Google Play services.
-Follows system health best practices.
-LiveData support to easily display work request state in UI.
回答5:
In my testing, JobScheduler could continue running a service after the user closes my app but I could find no way to do that with WorkManager.
I tested on a Nexus 5X running Oreo Android 8.0.0 (API 26). I may have just gotten lucky that this device/OS combination could continue the service after killing the app. I believe it might be device specific because of what I read in this answer https://stackoverflow.com/a/52605503/2848676 to Android. Is WorkManager running when app is closed?. "You can find a complete list of different OEM behaviors on dontkillmyapp.com."
NOTE: I observed that when the user closed the app, it took several seconds or even minutes for the JobService to restart.
To make JobScheduler launch a service that survives app death, created the service as follows:
JobScheduler jobScheduler = (JobScheduler)applicationContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
ComponentName componentName = new ComponentName(applicationContext, ScannerAsJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, componentName)
.setPersisted(true) // relaunch on reboot
.setMinimumLatency(1)
.setOverrideDeadline(1)
.build();
int result = jobScheduler.schedule(jobInfo);
ScannerAsJobService
updates a notification with current information, including whether the service is "in the background" (which I quote because there are different definitions of "background" but this one accurately reflected whether the app was no longer running):
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import androidx.annotation.RequiresApi;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class ScannerAsJobService extends JobService {
private static final String TAG = "ScannerAsJobService";
private static final String NOTIFICATION_CHANNEL_ID = "ll_notification_channel";
final Handler workHandler = new Handler();
Runnable workRunnable;
@Override
public boolean onStartJob(JobParameters params) {
workRunnable = new Runnable() {
@Override
public void run() {
// See https://stackoverflow.com/questions/45692181/android-job-scheduler-schedule-job-to-execute-immediately-and-exactly-once
(new Timer()).schedule(new TimerTask() {
@Override
public void run() {
String message = "Time: " + (new Date()).toString() + " background: " + appIsInBackground();
// https://stackoverflow.com/a/32346246/2848676
Intent intent = new Intent();
PendingIntent pendingIntent = PendingIntent.getActivity(ScannerAsJobService.this, 1, intent, 0);
Notification.Builder builder = new Notification.Builder(ScannerAsJobService.this);
builder.setAutoCancel(false);
builder.setTicker("my ticker info");
builder.setContentTitle("my content title");
builder.setContentText(message);
builder.setSmallIcon(R.drawable.my_icon);
builder.setContentIntent(pendingIntent);
// builder.setOngoing(true); // optionally uncomment this to prevent the user from being able to swipe away the notification
builder.setSubText("my subtext"); //API level 16
builder.setNumber(100);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setupNotificationChannel(NOTIFICATION_CHANNEL_ID);
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
}
builder.build();
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(93423, builder.build());
Log.d(TAG + " Notification message: " + message);
}
}, 1 * 1000, 5 * 1000);
jobFinished(params, true);
}
};
workHandler.post(workRunnable);
// return true so Android knows the workRunnable is continuing in the background
return true;
}
// https://stackoverflow.com/a/45692202/2848676
@RequiresApi(api = Build.VERSION_CODES.O)
private void setupNotificationChannel(String channelId) {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// The user-visible name of the channel.
CharSequence name = getString(R.string.my_channel_name);
// The user-visible description of the channel.
String description = getString(R.string.my_channel_name);
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(channelId, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
}
/**
* Source: <a href="https://stackoverflow.com/a/39589149/2848676">https://stackoverflow.com/a/39589149/2848676</a>
* @return true if the app is in the background, false otherwise
*/
private boolean appIsInBackground() {
ActivityManager.RunningAppProcessInfo myProcess = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
boolean isInBackground = myProcess.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
return isInBackground;
}
@Override
public boolean onStopJob(JobParameters params) {
// returning true schedules the job for retry (we're using it to implement periodicity since
// JobInfo.Builder.setPeriodic() didn't work
return true;
}
}