67

如果我们已经有一个JobScheduler以及一些具有相同功能的漂亮反向端口( AndroidJobFirebaseJobDispatcher ),为什么我们还需要新的 Android WorkManager ?它有什么杀手级功能吗?因为我没有看到任何让我想迁移到另一个调度程序的东西。

4

7 回答 7

57

“WorkManager 有很多不错的功能,但它的主要目标是在旧设备上使用 JobScheduler 的 API”……等等,但我们已经有了一些向后移植。他们怎么了?简而言之:

  1. FireaseJobDispatcher很好,但它需要 Google Play来安排工作,例如,如果我们以中国为目标,那就不好了。

  2. 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 工程师的积极支持而不是被弃用,请在下面的评论中写下:)

于 2018-06-25T20:11:25.847 回答
26

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

因此,使用WorkManager,您无需担心向后兼容性。除此之外,它还允许定义为运行作业而需要满足的约束,例如定义网络约束、电池电量、充电状态和存储电量。

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

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

于 2018-11-11T08:00:25.667 回答
21

首先,WorkManager用于可以推迟并需要保证执行的工作。考虑到向后兼容性JobScheduler仅适用于 API 23+。为了避免不得不进行向后兼容性工作,WorkManager 会为您执行以下操作:-

在此处输入图像描述

工作经理的特点

  • 有保证的约束感知执行
  • 尊重系统后台限制
  • Quarreable,您可以检查状态,即失败、成功等
  • 可链接的,例如 work-A 取决于 work-B -> Work graph
  • 机会主义,一旦满足约束就尽量执行,实际上不需要作业调度程序进行干预,即唤醒应用程序或等待JobSheduler批处理您的工作(如果您的流程已启动并正在运行)
  • 向后兼容,有或没有谷歌播放服务

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

引擎盖下的扩展架构

在此处输入图像描述

于 2020-03-28T15:23:45.897 回答
21

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

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

因此,如果您对当前的调度程序实现感到满意,并且我提到的两个功能没有增加价值,那么我还没有看到进行切换的巨大好处。但是,如果您正在编写新的东西,那么使用 WorkManager 可能是值得的。

于 2018-05-10T23:49:34.037 回答
3

在我的测试中,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;
    }
}
于 2019-10-31T16:54:39.543 回答
3

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.
于 2019-08-09T18:52:40.173 回答
0

在 Google 文档中,WorkManger 是以前所有后台任务(包括 FirebaseJobDispatcher、GcmNetworkManager 和 Job Scheduler)的推荐放置位置。

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

于 2020-06-11T01:57:06.063 回答