如果我们已经有一个JobScheduler以及一些具有相同功能的漂亮反向端口( AndroidJob和FirebaseJobDispatcher ),为什么我们还需要新的 Android WorkManager ?它有什么杀手级功能吗?因为我没有看到任何让我想迁移到另一个调度程序的东西。
7 回答
“WorkManager 有很多不错的功能,但它的主要目标是在旧设备上使用 JobScheduler 的 API”……等等,但我们已经有了一些向后移植。他们怎么了?简而言之:
FireaseJobDispatcher很好,但它需要 Google Play来安排工作,例如,如果我们以中国为目标,那就不好了。
Evernote 的AndroidJob是一个具有很多功能的优秀向后移植。恕我直言,这是安排任何工作的最佳选择。但是现在最新版本的库在底层使用了前面提到的 WorkManager。而且,不幸的是,该库迟早会被弃用:
如果你开始一个新项目,你应该使用 WorkManager 而不是这个库。您还应该开始将您的代码从这个库迁移到 WorkManager。在未来的某个时候,这个库将被弃用。
他们建议切换到 WorkManager,因为它提供了更多功能,并且他们还给了我们一个简短的比较:
| 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,最后 3 个功能非常有用,仅受 WorkManager支持。所以我最后一个问题的答案是肯定的,它确实有一些杀手锏:
- 无需谷歌播放
- 可查询的
- 可链式
- 投机取巧
要了解有关 WorkManager 的更多信息,一定要观看Sumir Kataria 的演讲
PS 如果有人知道为什么FirebaseJobDispatcher 受到 Google 工程师的积极支持而不是被弃用,请在下面的评论中写下:)
WorkManager
使用JobScheduler
服务来安排作业。如果JobScheduler
设备不支持,则使用 FirebaseJobDispatcher
服务。如果 FirebaseJobDispatcher
在设备上不可用,它将使用AlarmManager
和BroadcastReceiver
。
因此,使用WorkManager
,您无需担心向后兼容性。除此之外,它还允许定义为运行作业而需要满足的约束,例如定义网络约束、电池电量、充电状态和存储电量。
它允许任务链接和将参数传递给作业。
http://www.zoftino.com/scheduling-tasks-with-workmanager-in-android
首先,WorkManager
用于可以推迟并需要保证执行的工作。考虑到向后兼容性JobScheduler
仅适用于 API 23+。为了避免不得不进行向后兼容性工作,WorkManager 会为您执行以下操作:-
工作经理的特点
- 有保证的约束感知执行
- 尊重系统后台限制
- Quarreable,您可以检查状态,即失败、成功等
- 可链接的,例如 work-A 取决于 work-B -> Work graph
- 机会主义,一旦满足约束就尽量执行,实际上不需要作业调度程序进行干预,即唤醒应用程序或等待
JobSheduler
批处理您的工作(如果您的流程已启动并正在运行) - 向后兼容,有或没有谷歌播放服务
WorkManager 提供与 API 级别 14 的兼容性。WorkManager 会根据设备 API 级别选择适当的方式来安排后台任务。它可能使用 JobScheduler(在 API 23 和更高版本上)或 AlarmManager 和 BroadcastReceiver 的组合
引擎盖下的扩展架构
WorkManager 看起来就像 Google 对 Evernote 的 Android-Job 库的回应,但有一些改进。它使用 JobScheduler、Firebase JobDispatcher 和 AlarmManager,就像 Android-Job 一样,取决于设备的 API 级别。他们对标签的使用看起来几乎相同,并且为工作/工作分配约束也很相似。
我很兴奋的两个功能是:能够链接工作和在有限制的工作中投机取巧的能力。第一个将允许工作(工作)被分解并且对我来说更加模块化。随着更多的模块化工作,每件工作可能会有更少的限制,从而提高他们更早完成的机会(机会主义)。例如,大多数处理工作可以在需要满足网络约束的工作之前完成。
因此,如果您对当前的调度程序实现感到满意,并且我提到的两个功能没有增加价值,那么我还没有看到进行切换的巨大好处。但是,如果您正在编写新的东西,那么使用 WorkManager 可能是值得的。
在我的测试中,JobScheduler 可以在用户关闭我的应用程序后继续运行服务,但我找不到使用 WorkManager 的方法。
我在运行 Oreo Android 8.0.0 (API 26) 的 Nexus 5X 上进行了测试。我可能很幸运,这个设备/操作系统组合可以在杀死应用程序后继续服务。我相信它可能是特定于设备的,因为我在这个答案https://stackoverflow.com/a/52605503/2848676中读到了Android。关闭应用程序时 WorkManager 是否正在运行?. “您可以在dontkillmyapp.com上找到不同 OEM 行为的完整列表。”
注意:我观察到当用户关闭应用程序时,JobService 需要几秒钟甚至几分钟才能重新启动。
为了让 JobScheduler 启动一个在应用程序死亡后幸存的服务,创建该服务如下:
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
使用当前信息更新通知,包括服务是否“在后台”(我引用它是因为“后台”有不同的定义,但这个准确地反映了应用程序是否不再运行):
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;
}
}
WorkManager 位于 JobScheduler 和 AlarmManager 之上。WorkManager 根据用户设备 API 级别等条件选择要使用的正确 API
WorkManager 是一个简单但非常灵活的库,它有许多额外的好处。这些包括:
-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.
在 Google 文档中,WorkManger 是以前所有后台任务(包括 FirebaseJobDispatcher、GcmNetworkManager 和 Job Scheduler)的推荐放置位置。
见:https ://developer.android.com/topic/libraries/architecture/workmanager