From b077c9b4f325c2a4e5f12e1832c527a2ae030a64 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Tue, 19 Jan 2021 17:07:32 -0400 Subject: [PATCH] Only schedule one job per constraint set. --- .../securesms/jobmanager/Constraint.java | 10 ++++ .../jobmanager/JobSchedulerScheduler.java | 49 ++++++++----------- .../jobmanager/impl/ChargingConstraint.java | 5 ++ .../jobmanager/impl/NetworkConstraint.java | 6 +++ 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java index 68683944b..ad3c26646 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobmanager; import android.app.job.JobInfo; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; public interface Constraint { @@ -14,6 +15,15 @@ public interface Constraint { @RequiresApi(26) void applyToJobInfo(@NonNull JobInfo.Builder jobInfoBuilder); + /** + * If you do something in {@link #applyToJobInfo} you should return something here. + *

+ * It is sorted and concatenated with other constraints key parts to form a unique job id. + */ + default @Nullable String getJobSchedulerKeyPart() { + return null; + } + interface Factory { T create(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java index a50d677da..6080fc32c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java @@ -6,26 +6,23 @@ import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +import com.annimon.stream.Collectors; +import com.annimon.stream.Stream; + import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import java.util.List; +import java.util.Locale; @RequiresApi(26) -public class JobSchedulerScheduler implements Scheduler { +public final class JobSchedulerScheduler implements Scheduler { - private static final String TAG = JobSchedulerScheduler.class.getSimpleName(); - - private static final String PREF_NAME = "JobSchedulerScheduler_prefs"; - private static final String PREF_NEXT_ID = "pref_next_id"; - - private static final int MAX_ID = 20; + private static final String TAG = Log.tag(JobSchedulerScheduler.class); private final Application application; @@ -37,14 +34,23 @@ public class JobSchedulerScheduler implements Scheduler { @Override public void schedule(long delay, @NonNull List constraints) { JobScheduler jobScheduler = application.getSystemService(JobScheduler.class); - int currentId = getCurrentId(); - if (constraints.isEmpty() && jobScheduler.getPendingJob(currentId) != null) { - Log.d(TAG, "Skipping JobScheduler enqueue because we have no constraints and there's already one pending."); + String constraintNames = constraints.isEmpty() ? "" + : Stream.of(constraints) + .map(Constraint::getJobSchedulerKeyPart) + .withoutNulls() + .sorted() + .collect(Collectors.joining("-")); + + int jobId = constraintNames.hashCode(); + + if (jobScheduler.getPendingJob(jobId) != null) { return; } - JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(getAndUpdateNextId(), new ComponentName(application, SystemService.class)) + Log.i(TAG, String.format(Locale.US, "JobScheduler enqueue of %s (%d)", constraintNames, jobId)); + + JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(jobId, new ComponentName(application, SystemService.class)) .setMinimumLatency(delay) .setPersisted(true); @@ -55,21 +61,6 @@ public class JobSchedulerScheduler implements Scheduler { jobScheduler.schedule(jobInfoBuilder.build()); } - private int getCurrentId() { - SharedPreferences prefs = application.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); - return prefs.getInt(PREF_NEXT_ID, 0); - } - - private int getAndUpdateNextId() { - SharedPreferences prefs = application.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); - int returnedId = prefs.getInt(PREF_NEXT_ID, 0); - int nextId = returnedId + 1 > MAX_ID ? 0 : returnedId + 1; - - prefs.edit().putInt(PREF_NEXT_ID, nextId).apply(); - - return returnedId; - } - @RequiresApi(api = 26) public static class SystemService extends JobService { @@ -77,6 +68,8 @@ public class JobSchedulerScheduler implements Scheduler { public boolean onStartJob(JobParameters params) { JobManager jobManager = ApplicationDependencies.getJobManager(); + Log.i(TAG, "Waking due to job: " + params.getJobId()); + jobManager.addOnEmptyQueueListener(new JobManager.EmptyQueueListener() { @Override public void onQueueEmpty() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java index 40911c68b..e67f71c7c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java @@ -33,6 +33,11 @@ public class ChargingConstraint implements Constraint { jobInfoBuilder.setRequiresCharging(true); } + @Override + public String getJobSchedulerKeyPart() { + return "CHARGING"; + } + public static final class Factory implements Constraint.Factory { @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java index b1614787d..493223e8d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java @@ -7,6 +7,7 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import org.thoughtcrime.securesms.jobmanager.Constraint; @@ -37,6 +38,11 @@ public class NetworkConstraint implements Constraint { jobInfoBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); } + @Override + public String getJobSchedulerKeyPart() { + return "NETWORK"; + } + public static boolean isMet(@NonNull Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();