SimpleInMemoryPriorityQueue.java
package com.birbit.android.jobqueue.inMemoryQueue;
import android.support.annotation.NonNull;
import com.birbit.android.jobqueue.Constraint;
import com.birbit.android.jobqueue.JobHolder;
import com.birbit.android.jobqueue.JobQueue;
import com.birbit.android.jobqueue.Params;
import com.birbit.android.jobqueue.config.Configuration;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicLong;
/**
* A simple implementation of in memory {@link com.birbit.android.jobqueue.JobQueue}
*/
public class SimpleInMemoryPriorityQueue implements JobQueue {
private final TreeSet<JobHolder> jobs = new TreeSet<>(new Comparator<JobHolder>() {
@Override
public int compare(JobHolder holder1, JobHolder holder2) {
if (holder1.getJob().getId().equals(holder2.getJob().getId())) {
return 0;
}
int cmp = compareInt(holder1.getPriority(), holder2.getPriority());
if (cmp != 0) {
return cmp;
}
cmp = -compareLong(holder1.getCreatedNs(), holder2.getCreatedNs());
if (cmp != 0) {
return cmp;
}
//if jobs were created at the same time, smaller id first
return -compareLong(holder1.getInsertionOrder(), holder2.getInsertionOrder());
}
private int compareInt(int i1, int i2) {
if (i1 > i2) {
return -1;
}
if (i2 > i1) {
return 1;
}
return 0;
}
private int compareLong(long l1, long l2) {
if (l1 > l2) {
return -1;
}
if (l2 > l1) {
return 1;
}
return 0;
}
});
private final Map<String, JobHolder> idCache = new HashMap<>();
private final AtomicLong insertionOrderCounter = new AtomicLong(0);
private final List<String> reusedList = new ArrayList<>();
private final long sessionId;
public SimpleInMemoryPriorityQueue(
@SuppressWarnings("UnusedParameters") Configuration configuration, long sessionId) {
this.sessionId = sessionId;
}
@Override
public boolean insert(@NonNull JobHolder jobHolder) {
jobHolder.setInsertionOrder(insertionOrderCounter.incrementAndGet());
JobHolder existing = idCache.get(jobHolder.getId());
if (existing != null) {
throw new IllegalArgumentException("cannot add a job with the same id twice");
}
idCache.put(jobHolder.getId(), jobHolder);
jobs.add(jobHolder);
return true;
}
@Override
public boolean insertOrReplace(@NonNull JobHolder jobHolder) {
if (jobHolder.getInsertionOrder() == null) {
return insert(jobHolder);
}
JobHolder existing = idCache.get(jobHolder.getId());
if (existing != null) {
remove(existing);
}
idCache.put(jobHolder.getId(), jobHolder);
jobs.add(jobHolder);
return true;
}
@Override
public void substitute(@NonNull JobHolder newJob, @NonNull JobHolder oldJob) {
remove(oldJob);
insert(newJob);
}
@Override
public void remove(@NonNull JobHolder jobHolder) {
idCache.remove(jobHolder.getId());
jobs.remove(jobHolder);
}
@Override
public int count() {
return jobs.size();
}
@Override
public int countReadyJobs(@NonNull Constraint constraint) {
int count = 0;
reusedList.clear();
for (JobHolder holder : jobs) {
String groupId = holder.getGroupId();
if ((groupId == null || !reusedList.contains(groupId)) && matches(holder, constraint, false)) {
count++;
if (groupId != null) {
reusedList.add(groupId);
}
}
}
reusedList.clear();
return count;
}
@Override
public JobHolder nextJobAndIncRunCount(@NonNull Constraint constraint) {
for (JobHolder holder : jobs) {
if (matches(holder, constraint, false)) {
remove(holder);
holder.setRunCount(holder.getRunCount() + 1);
holder.setRunningSessionId(sessionId);
return holder;
}
}
return null;
}
@Override
public Long getNextJobDelayUntilNs(@NonNull Constraint constraint) {
Long minDelay = null;
for (JobHolder holder : jobs) {
if (matches(holder, constraint, true)) {
final boolean hasDelay = holder.hasDelay() && matches(holder, constraint, false);
final boolean hasDeadline = holder.hasDeadline();
final long delay;
if (hasDeadline == hasDelay) {
delay = Math.min(holder.getDeadlineNs(), holder.getDelayUntilNs());
} else if (hasDeadline) {
delay = holder.getDeadlineNs();
} else {
delay = holder.getDelayUntilNs();
}
if (minDelay == null || delay < minDelay) {
minDelay = delay;
}
}
}
return minDelay;
}
@Override
public void clear() {
jobs.clear();
idCache.clear();
}
@Override
public JobHolder findJobById(@NonNull String id) {
return idCache.get(id);
}
@NonNull
@Override
public Set<JobHolder> findJobs(@NonNull Constraint constraint) {
Set<JobHolder> result = new HashSet<>();
for (JobHolder holder : jobs) {
if (matches(holder, constraint, false)) {
result.add(holder);
}
}
return result;
}
@Override
public void onJobCancelled(JobHolder holder) {
remove(holder);
}
private static boolean matches(JobHolder holder, Constraint constraint, boolean acceptAnyDeadline) {
boolean hitDeadline = constraint.getNowInNs() >= holder.getDeadlineNs()
|| (acceptAnyDeadline && holder.hasDeadline());
if (!hitDeadline) {
if (constraint.getMaxNetworkType() < holder.getRequiredNetworkType()) {
return false;
}
}
if (constraint.getTimeLimit() != null && holder.getDelayUntilNs() > constraint.getTimeLimit()) {
return false;
}
if (holder.getGroupId() != null && constraint.getExcludeGroups().contains(holder.getGroupId())) {
return false;
}
if (constraint.getExcludeJobIds().contains(holder.getId())) {
return false;
}
//noinspection RedundantIfStatement
if (constraint.getTagConstraint() != null &&
(holder.getTags() == null || constraint.getTags().isEmpty() ||
!constraint.getTagConstraint().matches(constraint.getTags(), holder.getTags()))) {
return false;
}
return true;
}
}