Make HabitCardListCache more thread-safe; add null checks

pull/542/head
Alinson S. Xavier 6 years ago
parent d1b83d069d
commit 7c742e1016

@ -47,6 +47,7 @@ public class HabitCardListCache implements CommandRunner.Listener
{ {
private int checkmarkCount; private int checkmarkCount;
@Nullable
private Task currentFetchTask; private Task currentFetchTask;
@NonNull @NonNull
@ -56,13 +57,15 @@ public class HabitCardListCache implements CommandRunner.Listener
private CacheData data; private CacheData data;
@NonNull @NonNull
private HabitList allHabits; private final HabitList allHabits;
@NonNull @NonNull
private HabitList filteredHabits; private HabitList filteredHabits;
@NonNull
private final TaskRunner taskRunner; private final TaskRunner taskRunner;
@NonNull
private final CommandRunner commandRunner; private final CommandRunner commandRunner;
@Inject @Inject
@ -70,21 +73,27 @@ public class HabitCardListCache implements CommandRunner.Listener
@NonNull CommandRunner commandRunner, @NonNull CommandRunner commandRunner,
@NonNull TaskRunner taskRunner) @NonNull TaskRunner taskRunner)
{ {
if (allHabits == null) throw new NullPointerException();
if (commandRunner == null) throw new NullPointerException();
if (taskRunner == null) throw new NullPointerException();
this.allHabits = allHabits; this.allHabits = allHabits;
this.commandRunner = commandRunner; this.commandRunner = commandRunner;
this.filteredHabits = allHabits; this.filteredHabits = allHabits;
this.taskRunner = taskRunner; this.taskRunner = taskRunner;
this.listener = new Listener() {}; this.listener = new Listener()
{
};
data = new CacheData(); data = new CacheData();
} }
public void cancelTasks() public synchronized void cancelTasks()
{ {
if (currentFetchTask != null) currentFetchTask.cancel(); if (currentFetchTask != null) currentFetchTask.cancel();
} }
public int[] getCheckmarks(long habitId) public synchronized int[] getCheckmarks(long habitId)
{ {
return data.checkmarks.get(habitId); return data.checkmarks.get(habitId);
} }
@ -98,57 +107,57 @@ public class HabitCardListCache implements CommandRunner.Listener
@Nullable @Nullable
public synchronized Habit getHabitByPosition(int position) public synchronized Habit getHabitByPosition(int position)
{ {
if(position < 0 || position >= data.habits.size()) return null; if (position < 0 || position >= data.habits.size()) return null;
return data.habits.get(position); return data.habits.get(position);
} }
public int getHabitCount() public synchronized int getHabitCount()
{ {
return data.habits.size(); return data.habits.size();
} }
public HabitList.Order getOrder() public synchronized HabitList.Order getOrder()
{ {
return filteredHabits.getOrder(); return filteredHabits.getOrder();
} }
public double getScore(long habitId) public synchronized double getScore(long habitId)
{ {
return data.scores.get(habitId); return data.scores.get(habitId);
} }
public void onAttached() public synchronized void onAttached()
{ {
refreshAllHabits(); refreshAllHabits();
commandRunner.addListener(this); commandRunner.addListener(this);
} }
@Override @Override
public void onCommandExecuted(@NonNull Command command, public synchronized void onCommandExecuted(@Nullable Command command,
@Nullable Long refreshKey) @Nullable Long refreshKey)
{ {
if (refreshKey == null) refreshAllHabits(); if (refreshKey == null) refreshAllHabits();
else refreshHabit(refreshKey); else refreshHabit(refreshKey);
} }
public void onDetached() public synchronized void onDetached()
{ {
commandRunner.removeListener(this); commandRunner.removeListener(this);
} }
public void refreshAllHabits() public synchronized void refreshAllHabits()
{ {
if (currentFetchTask != null) currentFetchTask.cancel(); if (currentFetchTask != null) currentFetchTask.cancel();
currentFetchTask = new RefreshTask(); currentFetchTask = new RefreshTask();
taskRunner.execute(currentFetchTask); taskRunner.execute(currentFetchTask);
} }
public void refreshHabit(long id) public synchronized void refreshHabit(long id)
{ {
taskRunner.execute(new RefreshTask(id)); taskRunner.execute(new RefreshTask(id));
} }
public void remove(@NonNull Long id) public synchronized void remove(long id)
{ {
Habit h = data.id_to_habit.get(id); Habit h = data.id_to_habit.get(id);
if (h == null) return; if (h == null) return;
@ -162,7 +171,7 @@ public class HabitCardListCache implements CommandRunner.Listener
listener.onItemRemoved(position); listener.onItemRemoved(position);
} }
public void reorder(int from, int to) public synchronized void reorder(int from, int to)
{ {
Habit fromHabit = data.habits.get(from); Habit fromHabit = data.habits.get(from);
data.habits.remove(from); data.habits.remove(from);
@ -170,23 +179,26 @@ public class HabitCardListCache implements CommandRunner.Listener
listener.onItemMoved(from, to); listener.onItemMoved(from, to);
} }
public void setCheckmarkCount(int checkmarkCount) public synchronized void setCheckmarkCount(int checkmarkCount)
{ {
this.checkmarkCount = checkmarkCount; this.checkmarkCount = checkmarkCount;
} }
public void setFilter(HabitMatcher matcher) public synchronized void setFilter(@NonNull HabitMatcher matcher)
{ {
if (matcher == null) throw new NullPointerException();
filteredHabits = allHabits.getFiltered(matcher); filteredHabits = allHabits.getFiltered(matcher);
} }
public void setListener(@NonNull Listener listener) public synchronized void setListener(@NonNull Listener listener)
{ {
if (listener == null) throw new NullPointerException();
this.listener = listener; this.listener = listener;
} }
public void setOrder(HabitList.Order order) public synchronized void setOrder(@NonNull HabitList.Order order)
{ {
if (order == null) throw new NullPointerException();
allHabits.setOrder(order); allHabits.setOrder(order);
filteredHabits.setOrder(order); filteredHabits.setOrder(order);
refreshAllHabits(); refreshAllHabits();
@ -198,30 +210,40 @@ public class HabitCardListCache implements CommandRunner.Listener
*/ */
public interface Listener public interface Listener
{ {
default void onItemChanged(int position) {} default void onItemChanged(int position)
{
}
default void onItemInserted(int position) {} default void onItemInserted(int position)
{
}
default void onItemMoved(int oldPosition, int newPosition) {} default void onItemMoved(int oldPosition, int newPosition)
{
}
default void onItemRemoved(int position) {} default void onItemRemoved(int position)
{
}
default void onRefreshFinished() {} default void onRefreshFinished()
{
}
} }
private class CacheData private class CacheData
{ {
@NonNull @NonNull
public HashMap<Long, Habit> id_to_habit; public final HashMap<Long, Habit> id_to_habit;
@NonNull @NonNull
public List<Habit> habits; public final List<Habit> habits;
@NonNull @NonNull
public HashMap<Long, int[]> checkmarks; public final HashMap<Long, int[]> checkmarks;
@NonNull @NonNull
public HashMap<Long, Double> scores; public final HashMap<Long, Double> scores;
/** /**
* Creates a new CacheData without any content. * Creates a new CacheData without any content.
@ -234,8 +256,10 @@ public class HabitCardListCache implements CommandRunner.Listener
scores = new HashMap<>(); scores = new HashMap<>();
} }
public void copyCheckmarksFrom(@NonNull CacheData oldData) public synchronized void copyCheckmarksFrom(@NonNull CacheData oldData)
{ {
if (oldData == null) throw new NullPointerException();
int[] empty = new int[checkmarkCount]; int[] empty = new int[checkmarkCount];
for (Long id : id_to_habit.keySet()) for (Long id : id_to_habit.keySet())
@ -246,8 +270,10 @@ public class HabitCardListCache implements CommandRunner.Listener
} }
} }
public void copyScoresFrom(@NonNull CacheData oldData) public synchronized void copyScoresFrom(@NonNull CacheData oldData)
{ {
if (oldData == null) throw new NullPointerException();
for (Long id : id_to_habit.keySet()) for (Long id : id_to_habit.keySet())
{ {
if (oldData.scores.containsKey(id)) if (oldData.scores.containsKey(id))
@ -256,10 +282,11 @@ public class HabitCardListCache implements CommandRunner.Listener
} }
} }
public void fetchHabits() public synchronized void fetchHabits()
{ {
for (Habit h : filteredHabits) for (Habit h : filteredHabits)
{ {
if (h.getId() == null) continue;
habits.add(h); habits.add(h);
id_to_habit.put(h.getId(), h); id_to_habit.put(h.getId(), h);
} }
@ -269,13 +296,14 @@ public class HabitCardListCache implements CommandRunner.Listener
private class RefreshTask implements Task private class RefreshTask implements Task
{ {
@NonNull @NonNull
private CacheData newData; private final CacheData newData;
@Nullable @Nullable
private Long targetId; private final Long targetId;
private boolean isCancelled; private boolean isCancelled;
@Nullable
private TaskRunner runner; private TaskRunner runner;
public RefreshTask() public RefreshTask()
@ -292,13 +320,13 @@ public class HabitCardListCache implements CommandRunner.Listener
} }
@Override @Override
public void cancel() public synchronized void cancel()
{ {
isCancelled = true; isCancelled = true;
} }
@Override @Override
public void doInBackground() public synchronized void doInBackground()
{ {
newData.fetchHabits(); newData.fetchHabits();
newData.copyScoresFrom(data); newData.copyScoresFrom(data);
@ -307,7 +335,7 @@ public class HabitCardListCache implements CommandRunner.Listener
Timestamp dateTo = DateUtils.getToday(); Timestamp dateTo = DateUtils.getToday();
Timestamp dateFrom = dateTo.minus(checkmarkCount - 1); Timestamp dateFrom = dateTo.minus(checkmarkCount - 1);
runner.publishProgress(this, -1); if (runner != null) runner.publishProgress(this, -1);
for (int position = 0; position < newData.habits.size(); position++) for (int position = 0; position < newData.habits.size(); position++)
{ {
@ -318,35 +346,36 @@ public class HabitCardListCache implements CommandRunner.Listener
if (targetId != null && !targetId.equals(id)) continue; if (targetId != null && !targetId.equals(id)) continue;
newData.scores.put(id, habit.getScores().getTodayValue()); newData.scores.put(id, habit.getScores().getTodayValue());
newData.checkmarks.put(id, habit newData.checkmarks.put(
.getCheckmarks() id,
.getValues(dateFrom, dateTo)); habit.getCheckmarks().getValues(dateFrom, dateTo));
runner.publishProgress(this, position); runner.publishProgress(this, position);
} }
} }
@Override @Override
public void onAttached(@NonNull TaskRunner runner) public synchronized void onAttached(@NonNull TaskRunner runner)
{ {
if (runner == null) throw new NullPointerException();
this.runner = runner; this.runner = runner;
} }
@Override @Override
public void onPostExecute() public synchronized void onPostExecute()
{ {
currentFetchTask = null; currentFetchTask = null;
listener.onRefreshFinished(); listener.onRefreshFinished();
} }
@Override @Override
public void onProgressUpdate(int currentPosition) public synchronized void onProgressUpdate(int currentPosition)
{ {
if (currentPosition < 0) processRemovedHabits(); if (currentPosition < 0) processRemovedHabits();
else processPosition(currentPosition); else processPosition(currentPosition);
} }
private void performInsert(Habit habit, int position) private synchronized void performInsert(Habit habit, int position)
{ {
Long id = habit.getId(); Long id = habit.getId();
data.habits.add(position, habit); data.habits.add(position, habit);
@ -356,14 +385,17 @@ public class HabitCardListCache implements CommandRunner.Listener
listener.onItemInserted(position); listener.onItemInserted(position);
} }
private void performMove(Habit habit, int fromPosition, int toPosition) private synchronized void performMove(@NonNull Habit habit,
int fromPosition,
int toPosition)
{ {
if(habit == null) throw new NullPointerException();
data.habits.remove(fromPosition); data.habits.remove(fromPosition);
data.habits.add(toPosition, habit); data.habits.add(toPosition, habit);
listener.onItemMoved(fromPosition, toPosition); listener.onItemMoved(fromPosition, toPosition);
} }
private void performUpdate(Long id, int position) private synchronized void performUpdate(long id, int position)
{ {
double oldScore = data.scores.get(id); double oldScore = data.scores.get(id);
int[] oldCheckmarks = data.checkmarks.get(id); int[] oldCheckmarks = data.checkmarks.get(id);
@ -381,7 +413,7 @@ public class HabitCardListCache implements CommandRunner.Listener
listener.onItemChanged(position); listener.onItemChanged(position);
} }
private void processPosition(int currentPosition) private synchronized void processPosition(int currentPosition)
{ {
Habit habit = newData.habits.get(currentPosition); Habit habit = newData.habits.get(currentPosition);
Long id = habit.getId(); Long id = habit.getId();
@ -401,7 +433,7 @@ public class HabitCardListCache implements CommandRunner.Listener
} }
} }
private void processRemovedHabits() private synchronized void processRemovedHabits()
{ {
Set<Long> before = data.id_to_habit.keySet(); Set<Long> before = data.id_to_habit.keySet();
Set<Long> after = newData.id_to_habit.keySet(); Set<Long> after = newData.id_to_habit.keySet();

Loading…
Cancel
Save