Use unique workers

pull/513/head
Alex Baker 6 years ago
parent 7339d5dcb3
commit df9a1acac4

@ -18,6 +18,10 @@ public class DateTimeUtils {
return new DateTime();
}
public static long midnight() {
return newDateTime().plusDays(1).startOfDay().getMillis();
}
public static DateTime newDateTime(long timestamp) {
return new DateTime(timestamp);
}

@ -2,17 +2,30 @@ package org.tasks.injection;
import android.support.annotation.NonNull;
import androidx.work.Worker;
import javax.inject.Inject;
import org.tasks.analytics.Tracker;
import timber.log.Timber;
public abstract class InjectingWorker extends Worker {
@Inject Tracker tracker;
@NonNull
@Override
public Result doWork() {
Timber.d("%s.doWork()", getClass().getSimpleName());
JobComponent component =
((InjectingApplication) getApplicationContext()).getComponent().plus(new WorkModule());
inject(component);
return Result.SUCCESS;
try {
return run();
} catch (Exception e) {
tracker.reportException(e);
return Result.FAILURE;
}
}
protected abstract Result run();
protected abstract void inject(JobComponent component);
}

@ -2,6 +2,7 @@ package org.tasks.jobs;
import static com.google.common.collect.Iterables.skip;
import static com.google.common.collect.Lists.newArrayList;
import static com.todoroo.andlib.utility.DateUtilities.now;
import static java.util.Collections.emptyList;
import android.content.Context;
@ -12,13 +13,14 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.backup.TasksJsonExporter;
import org.tasks.injection.ForApplication;
import org.tasks.injection.JobComponent;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
public class BackupWork extends DailyWork {
public class BackupWork extends RepeatingWorker {
static final String BACKUP_FILE_NAME_REGEX = "auto\\.[-\\d]+\\.json";
static final FileFilter FILE_FILTER = f -> f.getName().matches(BACKUP_FILE_NAME_REGEX);
@ -31,6 +33,7 @@ public class BackupWork extends DailyWork {
@Inject Preferences preferences;
@Inject WorkManager workManager;
@SuppressWarnings("unused")
public BackupWork() {}
BackupWork(Context context, TasksJsonExporter tasksJsonExporter, Preferences preferences) {
@ -40,8 +43,9 @@ public class BackupWork extends DailyWork {
}
@Override
protected Result doDailyWork() {
protected Result run() {
startBackup(context);
preferences.setLong(R.string.p_last_backup, now());
return Result.SUCCESS;
}

@ -23,9 +23,7 @@ public class CleanupWork extends InjectingWorker {
@NonNull
@Override
public Result doWork() {
super.doWork();
public Result run() {
long[] tasks = getInputData().getLongArray(EXTRA_TASK_IDS);
if (tasks == null) {
Timber.e("No task ids provided");

@ -1,31 +0,0 @@
package org.tasks.jobs;
import android.support.annotation.NonNull;
import javax.inject.Inject;
import org.tasks.analytics.Tracker;
import org.tasks.injection.InjectingWorker;
public abstract class DailyWork extends InjectingWorker {
@Inject Tracker tracker;
@NonNull
@Override
public final Result doWork() {
super.doWork();
Result result;
try {
result = doDailyWork();
} catch (Exception e) {
tracker.reportException(e);
result = Result.FAILURE;
}
scheduleNext();
return result;
}
protected abstract Result doDailyWork();
protected abstract void scheduleNext();
}

@ -4,13 +4,13 @@ import javax.inject.Inject;
import org.tasks.LocalBroadcastManager;
import org.tasks.injection.JobComponent;
public class MidnightRefreshWork extends DailyWork {
public class MidnightRefreshWork extends RepeatingWorker {
@Inject WorkManager workManager;
@Inject LocalBroadcastManager localBroadcastManager;
@Override
protected Result doDailyWork() {
protected Result run() {
localBroadcastManager.broadcastRefresh();
return Result.SUCCESS;
}

@ -81,7 +81,7 @@ public class NotificationQueue {
workManager.cancelNotifications();
}
} else {
workManager.scheduleNotification(nextScheduledTime(), cancelCurrent);
workManager.scheduleNotification(nextScheduledTime());
}
}

@ -9,7 +9,7 @@ import org.tasks.injection.InjectingWorker;
import org.tasks.injection.JobComponent;
import org.tasks.preferences.Preferences;
public class NotificationWork extends InjectingWorker {
public class NotificationWork extends RepeatingWorker {
@Inject Preferences preferences;
@Inject Notifier notifier;
@ -17,8 +17,7 @@ public class NotificationWork extends InjectingWorker {
@NonNull
@Override
public Result doWork() {
super.doWork();
public Result run() {
if (!preferences.isCurrentlyQuietHours()) {
List<? extends NotificationQueueEntry> overdueJobs = notificationQueue.getOverdueJobs();
notifier.triggerTaskNotifications(overdueJobs);
@ -27,7 +26,6 @@ public class NotificationWork extends InjectingWorker {
throw new RuntimeException("Failed to remove jobs from queue");
}
}
notificationQueue.scheduleNext();
return Result.SUCCESS;
}
@ -35,4 +33,9 @@ public class NotificationWork extends InjectingWorker {
protected void inject(JobComponent component) {
component.inject(this);
}
@Override
protected void scheduleNext() {
notificationQueue.scheduleNext();
}
}

@ -7,17 +7,15 @@ import org.tasks.injection.InjectingWorker;
import org.tasks.injection.JobComponent;
import org.tasks.scheduling.RefreshScheduler;
public class RefreshWork extends InjectingWorker {
public class RefreshWork extends RepeatingWorker {
@Inject RefreshScheduler refreshScheduler;
@Inject LocalBroadcastManager localBroadcastManager;
@NonNull
@Override
public Result doWork() {
super.doWork();
public Result run() {
localBroadcastManager.broadcastRefresh();
refreshScheduler.scheduleNext();
return Result.SUCCESS;
}
@ -25,4 +23,9 @@ public class RefreshWork extends InjectingWorker {
protected void inject(JobComponent component) {
component.inject(this);
}
@Override
protected void scheduleNext() {
refreshScheduler.scheduleNext();
}
}

@ -0,0 +1,17 @@
package org.tasks.jobs;
import android.support.annotation.NonNull;
import org.tasks.injection.InjectingWorker;
public abstract class RepeatingWorker extends InjectingWorker {
@NonNull
@Override
public final Result doWork() {
Result result = super.doWork();
scheduleNext();
return result;
}
protected abstract void scheduleNext();
}

@ -21,9 +21,7 @@ public class SyncWork extends InjectingWorker {
@NonNull
@Override
public Result doWork() {
super.doWork();
public Result run() {
synchronized (LOCK) {
if (preferences.isSyncOngoing()) {
return Result.RETRY;

@ -1,13 +1,18 @@
package org.tasks.jobs;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import static com.todoroo.andlib.utility.DateUtilities.now;
import static org.tasks.date.DateTimeUtils.midnight;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.printTimestamp;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.OneTimeWorkRequest.Builder;
import androidx.work.PeriodicWorkRequest;
import androidx.work.Worker;
import com.google.common.primitives.Longs;
@ -20,7 +25,6 @@ import org.tasks.data.CaldavDao;
import org.tasks.data.GoogleTaskListDao;
import org.tasks.injection.ApplicationScope;
import org.tasks.preferences.Preferences;
import org.tasks.time.DateTime;
import timber.log.Timber;
@ApplicationScope
@ -61,11 +65,19 @@ public class WorkManager {
}
public void syncNow() {
workManager.enqueue(
Constraints constraints =
new Constraints.Builder()
.setRequiredNetworkType(
preferences.getBoolean(R.string.p_background_sync_unmetered_only, false)
? NetworkType.UNMETERED
: NetworkType.CONNECTED)
.build();
OneTimeWorkRequest request =
new OneTimeWorkRequest.Builder(SyncWork.class)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES)
.addTag(TAG_SYNC)
.build());
.setConstraints(constraints)
.build();
workManager.beginUniqueWork(TAG_SYNC, ExistingWorkPolicy.KEEP, request).enqueue();
}
public void updateBackgroundSync() {
@ -92,10 +104,11 @@ public class WorkManager {
}
private void scheduleBackgroundSynchronization(boolean enabled, boolean onlyOnUnmetered) {
cancelAllForTag(TAG_BACKGROUND_SYNC);
Timber.d("background sync enabled: %s, onlyOnUnmetered: %s", enabled, onlyOnUnmetered);
if (enabled) {
workManager.enqueue(
workManager.enqueueUniquePeriodicWork(
TAG_BACKGROUND_SYNC,
ExistingPeriodicWorkPolicy.KEEP,
new PeriodicWorkRequest.Builder(SyncWork.class, 1, TimeUnit.HOURS)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES)
.setConstraints(
@ -104,66 +117,49 @@ public class WorkManager {
onlyOnUnmetered ? NetworkType.UNMETERED : NetworkType.CONNECTED)
.build())
.build());
} else {
workManager.cancelUniqueWork(TAG_BACKGROUND_SYNC);
}
}
public void scheduleRefresh(long time) {
enqueue(RefreshWork.class, time, TAG_REFRESH);
enqueueUnique(TAG_REFRESH, RefreshWork.class, time);
}
void scheduleMidnightRefresh() {
enqueue(
MidnightRefreshWork.class,
new DateTime(currentTimeMillis()).plusDays(1).startOfDay().getMillis(),
TAG_MIDNIGHT_REFRESH);
enqueueUnique(TAG_MIDNIGHT_REFRESH, MidnightRefreshWork.class, midnight());
}
void scheduleNotification(long time, boolean cancelCurrent) {
if (cancelCurrent) {
cancelNotifications();
}
enqueue(NotificationWork.class, time, TAG_NOTIFICATION);
void scheduleNotification(long time) {
enqueueUnique(TAG_NOTIFICATION, NotificationWork.class, time);
}
void scheduleBackup() {
enqueue(
long lastBackup = preferences.getLong(R.string.p_last_backup, 0L);
enqueueUnique(
TAG_BACKUP,
BackupWork.class,
new DateTime(currentTimeMillis()).plusDays(1).startOfDay().getMillis(),
TAG_BACKUP);
}
private void enqueue(Class<? extends Worker> c, long time, String tag) {
long delay = calculateDelay(time);
Timber.d("enqueue %s: %s (%sms)", tag, printTimestamp(time), delay);
workManager.enqueue(
new OneTimeWorkRequest.Builder(c)
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
.addTag(tag)
.build());
Math.min(newDateTime(lastBackup).plusDays(1).getMillis(), midnight()));
}
public void cancelRefresh() {
cancelAllForTag(TAG_REFRESH);
private void enqueueUnique(String key, Class<? extends Worker> c, long time) {
long delay = time - now();
OneTimeWorkRequest.Builder builder = new Builder(c);
if (delay > 0) {
builder.setInitialDelay(delay, TimeUnit.MILLISECONDS);
}
Timber.d("%s: %s (%sms)", key, printTimestamp(time), delay);
workManager.beginUniqueWork(key, ExistingWorkPolicy.REPLACE, builder.build()).enqueue();
}
void cancelNotifications() {
cancelAllForTag(TAG_NOTIFICATION);
}
private void cancelAllForTag(String tag) {
Timber.d("cancelAllWorkByTag(%s)", tag);
workManager.cancelAllWorkByTag(tag);
}
private long calculateDelay(long time) {
return Math.max(5000, time - currentTimeMillis());
Timber.d("cancelNotifications");
workManager.cancelAllWorkByTag(TAG_NOTIFICATION);
}
public void onStartup() {
updateBackgroundSync();
cancelAllForTag(TAG_MIDNIGHT_REFRESH);
scheduleMidnightRefresh();
cancelAllForTag(TAG_BACKUP);
scheduleBackup();
}
}

@ -327,10 +327,18 @@ public class Preferences {
editor.commit();
}
public long getLong(int resourceId, long defValue) {
return getLong(context.getString(resourceId), defValue);
}
public long getLong(String key, long defValue) {
return prefs.getLong(key, defValue);
}
public void setLong(int resourceId, long value) {
setLong(context.getString(resourceId), value);
}
public void setLong(String key, long value) {
Editor editor = prefs.edit();
editor.putLong(key, value);

@ -46,7 +46,6 @@ public class RefreshScheduler {
boolean reschedule = upcoming.isEmpty() || timestamp < upcoming.first();
jobs.add(timestamp);
if (reschedule) {
workManager.cancelRefresh();
scheduleNext();
}
}

@ -292,4 +292,5 @@
<string name="p_background_sync_unmetered_only">background_sync_unmetered_only</string>
<string name="p_purchases">purchases</string>
<string name="p_sync_ongoing">sync_ongoing</string>
<string name="p_last_backup">last_backup</string>
</resources>

Loading…
Cancel
Save