【问题标题】:Scheduled job executes multiple time in Evernote- AndroidJob计划作业在 Evernote-AndroidJob 中多次执行
【发布时间】:2018-02-20 01:30:51
【问题描述】:

我想要运行一项定期作业,它是在 Evernote 的 Android 作业库的帮助下实现的。

我希望实现的是每 15 分钟更新一次 MyLocation。

问题是每 15 分钟,作业似乎执行多次。

我使用 OnePlus3 设备进行了测试,在调试过程中,我观察到 LocationUpdateJob.schedule() 只被调用一次,这是正确的,但 LocationUpdateJob.onRunJob() 被调用多次,这是不正确的,但应该每 15 分钟调用一次。

此外,根据 crashlytics 的说法,一些设备会抛出非法状态异常。 这个特殊的例外只发生在 Android 7 设备上。

这是崩溃报告中的崩溃:

    Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mydomain.myapp/MainActivity}: java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2947)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3008)
       at android.app.ActivityThread.-wrap14(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6688)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)
Caused by java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
       at android.os.Parcel.readException(Parcel.java:1701)
       at android.os.Parcel.readException(Parcel.java:1646)
       at android.app.job.IJobScheduler$Stub$Proxy.schedule(IJobScheduler.java:158)
       at android.app.JobSchedulerImpl.schedule(JobSchedulerImpl.java:42)
       at com.evernote.android.job.v21.JobProxy21.schedule(SourceFile:198)
       at com.evernote.android.job.v21.JobProxy21.plantPeriodic(SourceFile:92)
       at com.evernote.android.job.JobManager.scheduleWithApi(SourceFile:282)
       at com.evernote.android.job.JobManager.schedule(SourceFile:240)
       at com.evernote.android.job.JobRequest.schedule(SourceFile:366)
       at com.mydomain.myapp.service.locationUpdate.LocationUpdateJob.schedule(SourceFile:33)
       at com.mydomain.myapp.activities.HubActivity.onLoginSuccess(SourceFile:173)
       at com.mydomain.myapp.activities.HubActivity.onCreate(SourceFile:115)
       at android.app.Activity.performCreate(Activity.java:6912)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2900)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3008)
       at android.app.ActivityThread.-wrap14(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6688)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

这是我的代码:

应用类

@Override
public void onCreate() {
 //init things....
 JobManager
           .create(this)
           .addJobCreator(new LocationUpdateJobCreator());  
 }

LocationUpdateJobCreator

public class LocationUpdateJobCreator implements JobCreator {

    @Override
    public Job create(String s) {
        switch (s) {
            case LocationUpdateJob.TAG:
                return new LocationUpdateJob();
            default:
                return null;
        }
    }
}

主活动:

private void onLogin() {
   // do other things...
   LocationUpdateJob.schedule();
 }

LocationUpdateJob

public class LocationUpdateJob extends Job {

    public static final String TAG = "LocationUpdateJob";
    private static int jobId = -1;


    public static void schedule() {
        final long INTERVAL = 900000L;
        final long FLEX = 300000L;
        jobId = new JobRequest
                .Builder(LocationUpdateJob.TAG)
                .setPeriodic(INTERVAL, FLEX)
                .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
                .build()
                .schedule();
    }


    public static void stop() {
        JobManager
                .instance()
                .cancel(jobId);
    }


    @NonNull
    @Override
    protected Result onRunJob(Params params) {
        updateLocation();
        return Result.SUCCESS;
    }
}

我分叉了 Evernote 的示例项目,但他们执行完全相同的步骤,但我无法弄清楚我在做什么不同。

【问题讨论】:

标签: android cron-task evernote android-jobscheduler


【解决方案1】:

我找到了整个事情的答案。 我正在回答这个问题,以防其他人面临同样的问题。

好的,所以根据我上面的逻辑,这就是发生的事情:

1) 用户第一次打开应用程序,操作系统中安排了一个作业,该作业一直运行到无穷大。

2) 用户关闭并再次打开应用,操作系统中安排了另一个作业。

3) 到第 101 次用户打开应用程序时,现在已经安排了 100 个作业,应用程序开始安排第 101 个作业,这会引发异常,因为 android 只允许应用程序安排 100 个作业(在较新的操作系统版本中) )。

那么,我做了什么来解决这个问题?

我将schedule() 修改为如下所示:

public static void schedule() {
     final long INTERVAL = 900000L;
     final long FLEX = 300000L;
     jobId = new JobRequest
                    .Builder(LocationUpdateJob.TAG)
                    .setPeriodic(INTERVAL, 300000L)
                    .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
                    .setPersisted(true)
                    .setUpdateCurrent(true)
                    .build()
                    .schedule();
}

这里与我预定的旧方式不同的是:.setUpdateCurrent(true)

所以它的作用是,每次使用标签调度作业时,它都会用新作业替换具有相同标签的任何现有作业,因此只有一个作业被安排使用该标签执行,即作业的标记是唯一的。

有一个很简短很好的解释here,请阅读。

【讨论】:

  • setUpdateCurrent(true) 只有一个副作用对我不起作用:它从现在开始重新安排新工作。因此,如果说您的周期性工作每 24 小时发生一次,但该应用程序每天不断地被用户杀死两次,那么重新安排的工作将永远不会运行。所以我解决了这个问题,使用 JobManager.instance().getAllJobRequestsForTag(tag) 对同一个计划作业进行严格保护,否则使用 setUpdateCurrent(true)。
  • 这个讨论对我帮助很大..+1
  • 是否可以按时间顺序运行这两个作业?我有这个用例,我必须在交易(货币交易)后 60 秒在后台将数据更新到服务器。因此,假设我进行第一个事务并安排一个作业在 60 秒后运行,现在在这 60 秒内我进行另一个事务,为此我安排了另一个作业但具有相同的标签。我发现第二个覆盖第一个,它以某种方式让我想到如果没有任何排队功能要嵌入到这个库中。有吗?
  • 我认为这应该是一个单独的问题,但是,它被覆盖的原因是,如果你用相同的标签和 setUpdate(true) 创建作业,它将被替换
  • 大家好,enqueueUniqueWork(来自版本 1.0.0-alpha11)和enqueueUniquePeriodicWork(来自版本 1.0.0-alpha03)现已推出:stackoverflow.com/a/51083407/6086782
【解决方案2】:

对于 WorkManager,我在 .enqueue() 之前添加了 .beginUniqueWork(),它对我有用。

  Constraints myConstraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build();
  Data.Builder basicWorkerInputDataBuilder = new Data.Builder();

  OneTimeWorkRequest otwRequest = new OneTimeWorkRequest.Builder(BasicOneTimeWorker.class)                  
                .setConstraints(myConstraints).addTag("BasicOneTimeWorker")                    
                .setInputData(basicWorkerInputDataBuilder.build());

  WorkManager.getInstance().beginUniqueWork("uniqueBasicOneTimeWork",
                                            ExistingWorkPolicy.REPLACE,
                                           otwRequest).enqueue();

下面的讨论帮助我找到了解决方案。

https://github.com/googlecodelabs/android-workmanager/issues/16

https://issuetracker.google.com/issues/111569265

【讨论】:

    猜你喜欢
    • 2016-06-12
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-25
    • 2014-10-25
    相关资源
    最近更新 更多