62

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

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

4

4 回答 4

121

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

识别所有已注册的工作

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

  • 设置
  • 注册 XX 工作
  • 连接性
  • 警报
  • 闲置的
  • 电池
  • 应用空闲
  • 内容
  • 工作经历
  • 等待队列

您最有可能感兴趣的类别是Registered XX Jobs。这会告诉您设备上已安排了多少作业。
例如,你的包名是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

如果你使用FirebaseJobDispatcherlib 你可以使用

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.applicationJOB_ID1。您现在可以通过以下方式运行您的任务adb

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

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

Evernote Android 作业库

最后但当然不是最不重要的。
为此,请使用Evernote 的精彩库
它允许使用或根据您的 API 级别轻松向后移植较低的 API级别JobSchedulerJobSchedulerGcmNetworkManagerAlarmManager

编辑 24/08

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

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

我希望这会有所帮助。

谢谢

编辑 28/04/2019

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

编辑 202 年 10 月 6 日

Android Jetpack WorkManager 使诊断变得稍微容易一些。
首先,您需要为您的包启用诊断

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

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

adb logcat

或通过 Android StudioLogcat工具窗口

于 2017-02-09T09:50:07.410 回答
18

// 更新:Android Studio 2020.3.1 中有一个新的 GUI 工具:https ://developer.android.com/studio/preview/features#workmanager-inspector

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

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

@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设备的输出很长,信息更详细、可读性更好。还有更多的功能,如历史。缺点是你必须先找到有趣的部分。

我还没有找到强制运行作业的方法,但是有一个功能请求。请参阅 p4u144 的答案。

于 2016-08-15T14:15:19.870 回答
16

从 Android 7.0 开始,有一个新cmd的 adb shell。在 7.1 中(目前仅预览版),如您在此处看到adb shell cmd jobscheduler的那样,已添加强制运行 JobScheduler。帮助说:

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

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

于 2016-11-14T16:16:26.893 回答
-1

这对我有用,无需使用 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;
  }
}
于 2018-03-14T15:53:35.687 回答