"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 |
我认为,最后的三个功能是非常有用的,并且只有使用WorkManager才能支持。因此,对于我上一个问题的答案是肯定的,它确实具有一些强大的功能:
要了解有关WorkManager的更多信息,应该观看Sumir Kataria的这个演讲
附言:如果有人知道为什么FirebaseJobDispatcher由Google工程师积极支持而不是被弃用,请在下面的评论中写出来 :)
WorkManager
不会在旧设备上使用 JobScheduler
。 - Emmanuel MtaliWorkManager
使用JobScheduler
服务来调度作业。如果设备不支持JobScheduler
,则会使用Firebase JobDispatcher
服务。
如果设备上没有Firebase JobDispatcher
,则将使用AlarmManager
和BroadcastReceiver
。
因此,使用WorkManager
,您不需要担心向后兼容性。除此之外,它允许定义约束条件,需要满足这些条件才能运行作业,例如定义网络约束、电池电量、充电状态和存储级别。
它允许任务链接和传递参数到作业中。
http://www.zoftino.com/scheduling-tasks-with-workmanager-in-android
首先,WorkManager
用于可以延迟执行且需要保证执行的作业。针对向后兼容性,JobScheduler
仅适用于 API 23+。为避免需要处理向后兼容性问题,WorkManager
可为您完成此操作:
WorkManager 的功能
JobSheduler
批量处理工作也不必须WorkManager 可向后兼容至 API 级别 14。WorkManager 根据设备 API 级别选择适当的方式来调度后台任务。它可能会使用 JobScheduler(在 API 23 及更高版本上)或 AlarmManager 和 BroadcastReceiver 的组合。
扩展的底层架构
WorkManager似乎是谷歌对Evernote的Android-Job库做出的回应,并在其中进行了一些改进。它根据设备的API级别使用JobScheduler、Firebase JobDispatcher和AlarmManager,就像Android-Job一样。它们对标签的使用看起来基本相同,并且分配作业/工作的约束条件也足够相似。
我感到兴奋的两个特点是:能够链接工作和能够满足约束条件的机会性工作。第一个特点将允许工作(任务)被分解为更模块化的形式。随着工作更加模块化,每个工作单元可能会有更少的约束条件,从而提高了它们更早完成工作的机会(机会主义)。例如,大部分处理工作可以在需要满足网络约束条件的工作之前完成。
因此,如果您对当前的调度程序实现感到满意,并且我提到的这两个特点没有增加价值,那么我认为目前还没有切换的巨大好处。但是如果您正在编写新的代码,使用WorkManager可能是值得的。
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://dev59.com/0VcO5IYBdhLWcg3whR3M
(new Timer()).schedule(new TimerTask() {
@Override
public void run() {
String message = "Time: " + (new Date()).toString() + " background: " + appIsInBackground();
// https://dev59.com/5FwY5IYBdhLWcg3wq5Xk#32346246
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://dev59.com/C1cO5IYBdhLWcg3whiBx#45692202
@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://dev59.com/KHA65IYBdhLWcg3wuhIR#39589149">https://dev59.com/KHA65IYBdhLWcg3wuhIR#39589149</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之上运行。根据用户设备API级别等条件,WorkManager选择要使用的正确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.
WorkManager
是 Androidx 的一部分,而JobScheduler
则是旧的支持库的一部分。 - Sir Codesalot