Android WorkManager 与 JobScheduler

Android WorkManager vs JobScheduler

为什么我们需要具有相同功能的新 Android WorkManager if we already have a JobScheduler along with a few nifty backports (AndroidJob and FirebaseJobDispatcher)?它有任何杀手级功能吗?因为我没有看到 任何东西 让我想迁移到 yet another 调度程序。

WorkManager 看起来像是 Google 对 Evernote 的 Android-Job 库的回答,但有一些改进。它使用 JobScheduler、Firebase JobDispatcher 和 AlarmManager,就像 Android-Job 一样,具体取决于设备的 API 级别。他们对标签的使用看起来几乎相同,并且为 jobs/work 分配约束也足够相似。

令我兴奋的两个功能是:能够链接工作以及在有约束的情况下投机取巧的能力。第一个将允许工作(工作)被分解并且对我来说更加模块化。有了更多的模块化工作,每件工作可能会有更少的限制,从而提高他们更早完成的机会(机会主义)。例如,大部分处理工作可以在需要满足网络约束的工作之前完成。

因此,如果您对当前的调度程序实施感到满意,并且我提到的两个功能没有增加价值,那么我认为现在进行转换不会带来巨大好处。但是,如果您正在编写新内容,使用 WorkManager 可能是值得的。

"WorkManager has a lot of nice features but its main goal is to use the JobScheduler's API on older devices"...等等,但我们已经有了一些向后移植。他们怎么了?简而言之:

  1. FireaseJobDispatcher 很好,但它 需要 Google Play 来安排工作,如果我们针对中国,这就不好了, 例如.

  2. 印象笔记的 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.

他们建议切换到 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 支持。所以我最后一个问题的答案是肯定的,它确实有一些 killer-features:

  • 否Google需要玩
  • 可查询
  • 可链接
  • 投机取巧

要了解有关 WorkManager 的更多信息,一定要观看 Sumir Kataria 的 this talk

P.S。如果有人知道为什么 FirebaseJobDispatcher is actively supported by Google engineers 而不是被弃用,请写在下面的评论中:)

WorkManager 使用 JobScheduler 服务来安排作业。如果设备不支持 JobScheduler,则它使用 Firebase JobDispatcher 服务。 如果 Firebase JobDispatcher 在设备上不可用,它将使用 AlarmManagerBroadcastReceiver

因此使用 WorkManager,您无需担心向后兼容性问题。除此之外,它还允许定义约束条件,这些约束条件需要满足才能使作业 运行,例如定义网络约束条件、电池电量、充电状态和存储级别。

它允许任务链接并将参数传递给作业。

http://www.zoftino.com/scheduling-tasks-with-workmanager-in-android

WorkManager 位于 JobScheduler 和 AlarmManager 之上。 WorkManager 根据用户设备 API 级别

等条件选择要使用的正确 APIs

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.

在我的测试中,JobScheduler 可以在用户关闭我的应用程序后继续 运行 服务,但我找不到使用 WorkManager 的方法。

我在 Nexus 5X 运行 Oreo Android 8.0.0 (API 26) 上进行了测试。我可能很幸运,这个 device/OS 组合可以在终止应用程序后继续服务。我相信它可能是特定于设备的,因为我在这个答案 to . "You can find a complete list of different OEM behaviors on dontkillmyapp.com."

中读到了

注意:我观察到当用户关闭应用程序时,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 使用当前信息更新通知,包括服务是否为 "in the background"(我引用它是因为 "background" 有不同的定义,但这个准确反映了应用程序是否不再是 运行):

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 
                (new Timer()).schedule(new TimerTask() {
                    @Override
                    public void run() {
                        String message = "Time: " + (new Date()).toString() + " background: " + appIsInBackground();

                        // 
                        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;
    }

    // 
    @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=""></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 仅适用于 API 23+。为了避免必须处理向后兼容性,WorkManager 会为您做到这一点:-

工作管理器的特点

  • 有保证的约束感知执行
  • 尊重系统后台限制
  • Quarreable,您可以检查状态,即失败、成功等
  • 可链接,例如工作-A 依赖于工作-B -> 工作图
  • 投机取巧,尽量在满足约束条件时尽快执行,实际上不需要作业调度程序进行干预,即唤醒应用程序或等待 JobSheduler 来批处理您的工作,如果您的进程已启动且 运行
  • 向后兼容,有或没有google播放服务

WorkManager 提供回到 API 级别 14 的兼容性。WorkManager 根据设备 API 级别选择合适的方式来安排后台任务。它可能使用 JobScheduler(在 API 23 及更高版本上)或 AlarmManager 和 BroadcastReceiver

的组合

引擎盖架构下的扩展

在 Google 文档中,WorkManger 是之前所有后台任务的推荐位置,包括 Firebase JobDispatcher、Gcm Network Manager 和 Job Scheduler。

参见:https://developer.android.com/topic/libraries/architecture/workmanager