【问题标题】:What tools are available to test JobScheduler?有哪些工具可用于测试 JobScheduler?
【发布时间】:2016-08-10 18:28:34
【问题描述】:

我们正在通过JobScheduler 实现一个作业,用于后台加载数据。这项工作每天大约会触发一次。我们可以使用哪些工具来测试此功能(可能是 ADB)?

用例能够模拟作业运行所需的条件,或者只是明确地说“运行此作业”作为我们自动化测试套件的一部分。

【问题讨论】:

    标签: android job-scheduling android-jobscheduler


    【解决方案1】:

    没错。
    Henning 和 P4u144 让我走上了正确的道路,可以更详细地回答这个问题。

    识别所有已注册的职位

    使用adb shell dumpsys jobscheduler 命令确定您的任务。
    这将为您提供以下类别的大量输出。

    • 设置
    • 已注册 XX 职位
    • 连接性
    • 警报
    • 空闲
    • 电池
    • AppIdle
    • 内容
    • 工作经历
    • 等待队列

    您最有可能感兴趣的类别是已注册 XX 工作。这会告诉您设备上安排了多少作业。
    例如,您的包名是com.foo.bar.application,您应该会看到这样的条目:

    JOB #u0a93/17: eec3709 com.foo.bar.application/com.evernote.android.job.v21.PlatformJobService
        u0a93 tag=*job*/com.foo.bar.application/com.evernote.android.job.v21.PlatformJobService
        Source: uid=u0a93 user=0 pkg=com.foo.bar.application
        JobInfo:
          Service: com.foo.bar.application/com.evernote.android.job.v21.PlatformJobService
          PERIODIC: interval=+15m0s0ms flex=+5m0s0ms
          PERSISTED
          Requires: charging=false deviceIdle=false
          Network type: 2
          Backoff: policy=1 initial=+30s0ms
          Has early constraint
          Has late constraint
        Required constraints: TIMING_DELAY DEADLINE UNMETERED
        Satisfied constraints: CONNECTIVITY NOT_ROAMING APP_NOT_IDLE DEVICE_NOT_DOZING
        Unsatisfied constraints: TIMING_DELAY DEADLINE UNMETERED
        Earliest run time: 07:23
        Latest run time: 12:23
        Ready: false (job=false pending=false active=false user=true)
    

    提示:使用adb shell dumpsys jobscheduler | grep com.foo.bar.application 快速过滤列表。

    现在您可以轻松确定您的工作是否已按照正确的标准注册。

    FireBaseJobdispatcher

    如果你使用FirebaseJobDispatcher lib 你可以使用

    adb shell dumpsys activity service GcmService | grep com.foo.bar.debug
        com.foo.bar.debug:0 v853
        u0|com.foo.bar.debug: 3
        (scheduled) com.foo.bar.debug/com.firebase.jobdispatcher.GooglePlayReceiver{u=0 tag="com.foo.bar.debug.job.FetchArticlesJob" trigger=window{start=10800s,end=11700s,earliest=10448s,latest=11348s} requirements=[NET_UNMETERED,DEVICE_IDLE] attributes=[PERSISTED,RECURRING] scheduled=-351s last_run=N/A jid=N/A status=PENDING retries=0 client_lib=FIREBASE_JOB_DISPATCHER-1}
        (scheduled) com.foo.bar.debug/com.firebase.jobdispatcher.GooglePlayReceiver{u=0 tag="com.foo.bar.debug.job.FetchNotificationGroupsJob" trigger=window{start=86400s,end=129600s,earliest=86048s,latest=129248s} requirements=[NET_CONNECTED,CHARGING] attributes=[PERSISTED,RECURRING] scheduled=-351s last_run=N/A jid=N/A status=PENDING retries=0 client_lib=FIREBASE_JOB_DISPATCHER-1}
        (scheduled) com.foo.bar.debug/com.firebase.jobdispatcher.GooglePlayReceiver{u=0 tag="com.foo.bar.debug.job.RemoveUnusedRealmArticlesJob" trigger=window{start=577980s,end=608400s,earliest=521961s,latest=552381s} requirements=[NET_ANY] attributes=[PERSISTED,RECURRING] scheduled=-56018s last_run=N/A jid=N/A status=PENDING retries=0 client_lib=FIREBASE_JOB_DISPATCHER-1}
        (finished) [com.foo.bar.debug/com.firebase.jobdispatcher.GooglePlayReceiver:com.foo.bar.debug.job.UpdateNotificationGroupJob,u0]
        (finished) [com.foo.bar.debug/com.firebase.jobdispatcher.GooglePlayReceiver:com.foo.bar.debug.job.UpdatePushTokenJob,u0]
        (finished) [com.foo.bar.debug/com.firebase.jobdispatcher.GooglePlayReceiver:com.foo.bar.debug.job.FetchArticlesJob,u0]
    

    检查您的服务是否已安排或运行。

    强制你的任务运行

    创建Job 时,您会返回一个JOB_ID
    使用此JOB_ID 强制运行作业。
    您可以使用adb shell cmd jobscheduler run 命令执行此操作(需要 Android 7.1 或更高版本)。

    例如,您的包名是 com.foo.bar.application,而 JOB_ID 是 1。 您现在可以通过adb 运行您的任务

    adb shell cmd jobscheduler run -f com.foo.bar.application 1
    

    不要忘记-f 选项,因为即使不满足设置的限制,这也会强制作业运行。

    Evernote Android 作业库

    最后但同样重要的是。
    为此请使用the wonderful library from Evernote
    它允许使用 JobSchedulerGcmNetworkManagerAlarmManager 在较低的 API 级别上轻松向后移植 JobScheduler,具体取决于您的 API 级别。

    24/08 编辑

    更好地使用 firebase 作业调度程序库。

    Firebase JobDispatcher 是一个用于在 Android 应用中调度后台作业的库。它提供了一个兼容 JobScheduler 的 API,适用于安装了 Google Play 服务的所有最新版本的 Android(API 级别 9+)。

    我希望这会有所帮助。

    谢谢

    2019 年 4 月 28 日编辑

    Evernote Android-JobFirebase JobDispatcher 现在都处于仅维护模式,他们都建议使用 jetpack 的 WorkManager 进行此类工作。

    编辑 10/06/202

    Android Jetpack WorkManager 使诊断变得更加容易。
    首先你需要为你的包启用诊断

    adb shell am broadcast -a 'androidx.work.diagnostics.REQUEST_DIAGNOSTICS' -p 'com.foo.bar.application'
    

    之后你可以WorkManager将信息转储到ADB Logcat,你可以通过输入来观察。

    adb logcat
    

    或通过 Android Studio Logcat 工具窗口

    【讨论】:

    • 好总结!我只是想补充一点,你写的所有内容都适用于 Android 7 及更高版本。 JobScheduler 自 Android 5 起就已启用,但输出有所不同。只要有人想知道。
    • 在我的例子中,Firebase JobDispatcher 使用 GcmNetworkManager 来安排作业,即使在 Android O 上,而不是平台可用的 JobScheduler - 所以我不能使用这个命令来强制作业运行。不得不切换到 Evernote 的 android-job,它确实在 api 21+ 上使用平台 JobScheduler。
    • 输出确实显示Running job [FORCED],但作业没有运行。在带有 Android O 的模拟器上测试。
    • JOB #u0a93/17 JOB_ID 是 17 吗?
    • @MuhammadBabar 我在 Android 8.1 和 9(AVD 和设备)上遇到了同样的情况,但它也会关闭 logcat 输出,留下一个空白窗口。之后我不得不重新启动 AS。
    【解决方案2】:

    // 更新: Android Studio 2020.3.1 新增 GUI 工具:https://developer.android.com/studio/preview/features#workmanager-inspector

    // 旧答案: 使用命令adb shell dumpsys jobscheduler,您可以获得有关当前计划和活动作业的信息。

    我注意到命令的输出在 Android 6 和 7 之间有很大不同。对于 Android 5 设备,输出非常简短,有时甚至很晦涩。注册工作的有趣部分是构建here,为方便起见,在下面重复,这应该有助于破译:

    @Override
    public String toString() {
        return String.valueOf(hashCode()).substring(0, 3) + ".."
                + ":[" + job.getService()
                + ",jId=" + job.getId()
                + ",u" + getUserId()
                + ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)
                + "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")"
                + ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging()
                + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
                + ",P=" + job.isPersisted()
                + (isReady() ? "(READY)" : "")
                + "]";
    }
    
    另一方面,

    Android 7 设备的输出很长,信息更详细,可读性更好。还有更多的功能,如历史。缺点是你必须先找到有趣的部分。

    我还没有找到强制运行作业的方法,there is a feature request for it.请参阅 p4u144 的答案。

    【讨论】:

    • Henning:“Android 6”代码的链接实际上指向了 lollipop-release 分支,它与 marshmallow 分支的输出略有不同。
    【解决方案3】:

    从 Android 7.0 开始,有一个新的 cmd 用于 adb shell。在 7.1(目前仅预览版)中,添加了 adb shell cmd jobscheduler as you can see here 以强制运行您的 JobScheduler。 帮助说:

    作业调度器(jobscheduler)命令:帮助 打印此帮助文本。

    运行 [-f | --force] [-u | --user USER_ID] PACKAGE JOB_ID 触发特定计划作业的立即执行。 选项: -f 或 --force:即使存在技术限制,也可以运行作业,例如 当前不满足连接性 -u 或 --user:指定要运行哪个用户的作业;默认是 主用户或系统用户

    【讨论】:

      【解决方案4】:

      这对我有用,不需要使用 adb 命令。它需要 minSdk 21

      @RunWith(AndroidJUnit4.class)
      @TargetApi(VERSION_CODES.LOLLIPOP)
      public abstract class BaseJobServiceTest {
      
        protected final Context context() {
          return InstrumentationRegistry.getTargetContext();
        }
      
        protected final void launchJobAndWait(JobInfo jobInfo) throws InterruptedException {
          JobScheduler scheduler = (JobScheduler) context().getSystemService(Context.JOB_SCHEDULER_SERVICE);
      
          scheduler.schedule(jobInfo);
      
          while (jobExecutionPending(scheduler, jobInfo)) {
            Thread.sleep(50);
          }
        }
      
        private boolean jobExecutionPending(JobScheduler scheduler, JobInfo jobInfo) {
          if (VERSION.SDK_INT >= VERSION_CODES.N) {
            return scheduler.getPendingJob(jobInfo.getId()) != null;
          }
      
          List<JobInfo> scheduledJobs = scheduler.getAllPendingJobs();
          for (int i = 0, size = scheduledJobs.size(); i < size; i++) {
            if (scheduledJobs.get(i).getId() == jobInfo.getId()) {
              return true;
            }
          }
      
          return false;
        }
      }
      

      【讨论】:

      • 对于定期作业,scheduler.getPendingJob() 不会总是返回非空值吗?
      • 可能是这样,我现在没有使用它,所以我不能说。也许有一种方法可以确定它是否是新的 JobInfo?我不知道equals是否能胜任这项工作。
      • 这是一个测试类,我们要强制主线程进入休眠状态。
      猜你喜欢
      • 2010-12-06
      • 2010-09-17
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多