Improve performance of detail page by cancelling useless async tasks

pull/538/head
Alinson S. Xavier 6 years ago
parent e94e0c057e
commit cc05543692

@ -54,9 +54,6 @@ public class BarCard extends HabitCard
@BindView(R.id.title) @BindView(R.id.title)
TextView title; TextView title;
@Nullable
private TaskRunner taskRunner;
private int bucketSize; private int bucketSize;
public BarCard(Context context) public BarCard(Context context)
@ -85,29 +82,13 @@ public class BarCard extends HabitCard
refreshData(); refreshData();
} }
@Override
protected void refreshData()
{
if (taskRunner == null) return;
taskRunner.execute(new RefreshTask(getHabit()));
}
private void init() private void init()
{ {
inflate(getContext(), R.layout.show_habit_bar, this); inflate(getContext(), R.layout.show_habit_bar, this);
ButterKnife.bind(this); ButterKnife.bind(this);
boolSpinner.setSelection(1); boolSpinner.setSelection(1);
numericalSpinner.setSelection(2); numericalSpinner.setSelection(2);
bucketSize = 7; bucketSize = 7;
Context appContext = getContext().getApplicationContext();
if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
taskRunner = app.getComponent().getTaskRunner();
}
if (isInEditMode()) initEditMode(); if (isInEditMode()) initEditMode();
} }
@ -119,11 +100,17 @@ public class BarCard extends HabitCard
chart.populateWithRandomData(); chart.populateWithRandomData();
} }
private class RefreshTask implements Task @Override
protected Task createRefreshTask()
{
return new RefreshTask(getHabit());
}
private class RefreshTask extends CancelableTask
{ {
private final Habit habit; private final Habit habit;
public RefreshTask(Habit habit) RefreshTask(Habit habit)
{ {
this.habit = habit; this.habit = habit;
} }
@ -131,6 +118,7 @@ public class BarCard extends HabitCard
@Override @Override
public void doInBackground() public void doInBackground()
{ {
if (isCanceled()) return;
List<Checkmark> checkmarks; List<Checkmark> checkmarks;
if (bucketSize == 1) checkmarks = habit.getCheckmarks().getAll(); if (bucketSize == 1) checkmarks = habit.getCheckmarks().getAll();
else checkmarks = habit.getCheckmarks().groupBy(getTruncateField(bucketSize)); else checkmarks = habit.getCheckmarks().groupBy(getTruncateField(bucketSize));

@ -43,9 +43,6 @@ public class FrequencyCard extends HabitCard
@BindView(R.id.frequencyChart) @BindView(R.id.frequencyChart)
FrequencyChart chart; FrequencyChart chart;
@Nullable
private TaskRunner taskRunner;
public FrequencyCard(Context context) public FrequencyCard(Context context)
{ {
super(context); super(context);
@ -59,24 +56,15 @@ public class FrequencyCard extends HabitCard
} }
@Override @Override
protected void refreshData() protected Task createRefreshTask()
{ {
if(taskRunner == null) return; return new RefreshTask();
taskRunner.execute(new RefreshTask());
} }
private void init() private void init()
{ {
inflate(getContext(), R.layout.show_habit_frequency, this); inflate(getContext(), R.layout.show_habit_frequency, this);
ButterKnife.bind(this); ButterKnife.bind(this);
Context appContext = getContext().getApplicationContext();
if(appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
taskRunner = app.getComponent().getTaskRunner();
}
if (isInEditMode()) initEditMode(); if (isInEditMode()) initEditMode();
} }
@ -88,11 +76,12 @@ public class FrequencyCard extends HabitCard
chart.populateWithRandomData(); chart.populateWithRandomData();
} }
private class RefreshTask implements Task private class RefreshTask extends CancelableTask
{ {
@Override @Override
public void doInBackground() public void doInBackground()
{ {
if (isCanceled()) return;
RepetitionList reps = getHabit().getRepetitions(); RepetitionList reps = getHabit().getRepetitions();
HashMap<Timestamp, Integer[]> frequency = reps.getWeekdayFrequency(); HashMap<Timestamp, Integer[]> frequency = reps.getWeekdayFrequency();
chart.setFrequency(frequency); chart.setFrequency(frequency);

@ -24,8 +24,10 @@ import android.support.annotation.*;
import android.util.*; import android.util.*;
import android.widget.*; import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*; import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.core.tasks.*;
public abstract class HabitCard extends LinearLayout public abstract class HabitCard extends LinearLayout
implements ModelObservable.Listener implements ModelObservable.Listener
@ -33,6 +35,12 @@ public abstract class HabitCard extends LinearLayout
@NonNull @NonNull
private Habit habit; private Habit habit;
@Nullable
private TaskRunner taskRunner;
@Nullable
private Task currentRefreshTask;
public HabitCard(Context context) public HabitCard(Context context)
{ {
super(context); super(context);
@ -82,7 +90,15 @@ public abstract class HabitCard extends LinearLayout
super.onDetachedFromWindow(); super.onDetachedFromWindow();
} }
protected abstract void refreshData(); protected void refreshData()
{
if(taskRunner == null) return;
if(currentRefreshTask != null) currentRefreshTask.cancel();
currentRefreshTask = createRefreshTask();
taskRunner.execute(currentRefreshTask);
}
protected abstract Task createRefreshTask();
private void attachTo(Habit habit) private void attachTo(Habit habit)
{ {
@ -99,5 +115,11 @@ public abstract class HabitCard extends LinearLayout
private void init() private void init()
{ {
if(!isInEditMode()) habit = new MemoryModelFactory().buildHabit(); if(!isInEditMode()) habit = new MemoryModelFactory().buildHabit();
Context appContext = getContext().getApplicationContext();
if(appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
taskRunner = app.getComponent().getTaskRunner();
}
} }
} }

@ -44,9 +44,6 @@ public class HistoryCard extends HabitCard
@NonNull @NonNull
private Controller controller; private Controller controller;
@Nullable
private TaskRunner taskRunner;
public HistoryCard(Context context) public HistoryCard(Context context)
{ {
super(context); super(context);
@ -70,25 +67,10 @@ public class HistoryCard extends HabitCard
this.controller = controller; this.controller = controller;
} }
@Override
protected void refreshData()
{
if(taskRunner == null) return;
taskRunner.execute(new RefreshTask(getHabit()));
}
private void init() private void init()
{ {
inflate(getContext(), R.layout.show_habit_history, this); inflate(getContext(), R.layout.show_habit_history, this);
ButterKnife.bind(this); ButterKnife.bind(this);
Context appContext = getContext().getApplicationContext();
if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
taskRunner = app.getComponent().getTaskRunner();
}
controller = new Controller() {}; controller = new Controller() {};
if (isInEditMode()) initEditMode(); if (isInEditMode()) initEditMode();
} }
@ -101,20 +83,30 @@ public class HistoryCard extends HabitCard
chart.populateWithRandomData(); chart.populateWithRandomData();
} }
@Override
protected Task createRefreshTask()
{
return new RefreshTask(getHabit());
}
public interface Controller public interface Controller
{ {
default void onEditHistoryButtonClick() {} default void onEditHistoryButtonClick() {}
} }
private class RefreshTask implements Task private class RefreshTask extends CancelableTask
{ {
private final Habit habit; private final Habit habit;
public RefreshTask(Habit habit) {this.habit = habit;} private RefreshTask(Habit habit)
{
this.habit = habit;
}
@Override @Override
public void doInBackground() public void doInBackground()
{ {
if (isCanceled()) return;
int checkmarks[] = habit.getCheckmarks().getAllValues(); int checkmarks[] = habit.getCheckmarks().getAllValues();
chart.setCheckmarks(checkmarks); chart.setCheckmarks(checkmarks);
} }

@ -60,9 +60,6 @@ public class OverviewCard extends HabitCard
private int color; private int color;
@Nullable
private TaskRunner taskRunner;
public OverviewCard(Context context) public OverviewCard(Context context)
{ {
super(context); super(context);
@ -75,13 +72,6 @@ public class OverviewCard extends HabitCard
init(); init();
} }
@Override
protected void refreshData()
{
if(taskRunner == null) return;
taskRunner.execute(new RefreshTask());
}
private String formatPercentageDiff(float percentageDiff) private String formatPercentageDiff(float percentageDiff)
{ {
return String.format("%s%.0f%%", (percentageDiff >= 0 ? "+" : "\u2212"), return String.format("%s%.0f%%", (percentageDiff >= 0 ? "+" : "\u2212"),
@ -90,17 +80,9 @@ public class OverviewCard extends HabitCard
private void init() private void init()
{ {
Context appContext = getContext().getApplicationContext();
if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
taskRunner = app.getComponent().getTaskRunner();
}
inflate(getContext(), R.layout.show_habit_overview, this); inflate(getContext(), R.layout.show_habit_overview, this);
ButterKnife.bind(this); ButterKnife.bind(this);
cache = new Cache(); cache = new Cache();
if (isInEditMode()) initEditMode(); if (isInEditMode()) initEditMode();
} }
@ -146,20 +128,27 @@ public class OverviewCard extends HabitCard
private class Cache private class Cache
{ {
public float todayScore; float todayScore;
float lastMonthScore;
public float lastMonthScore; float lastYearScore;
public float lastYearScore; long totalCount;
}
public long totalCount; @Override
protected Task createRefreshTask()
{
return new RefreshTask();
} }
private class RefreshTask implements Task private class RefreshTask extends CancelableTask
{ {
@Override @Override
public void doInBackground() public void doInBackground()
{ {
if (isCanceled()) return;
Habit habit = getHabit(); Habit habit = getHabit();
ScoreList scores = habit.getScores(); ScoreList scores = habit.getScores();
@ -177,6 +166,7 @@ public class OverviewCard extends HabitCard
@Override @Override
public void onPostExecute() public void onPostExecute()
{ {
if (isCanceled()) return;
refreshScore(); refreshScore();
} }

@ -52,9 +52,6 @@ public class ScoreCard extends HabitCard
private int bucketSize; private int bucketSize;
@Nullable
private TaskRunner taskRunner;
@Nullable @Nullable
private Preferences prefs; private Preferences prefs;
@ -94,13 +91,6 @@ public class ScoreCard extends HabitCard
refreshData(); refreshData();
} }
@Override
protected void refreshData()
{
if(taskRunner == null) return;
taskRunner.execute(new RefreshTask());
}
private int getDefaultSpinnerPosition() private int getDefaultSpinnerPosition()
{ {
if(prefs == null) return 0; if(prefs == null) return 0;
@ -113,7 +103,6 @@ public class ScoreCard extends HabitCard
if (appContext instanceof HabitsApplication) if (appContext instanceof HabitsApplication)
{ {
HabitsApplication app = (HabitsApplication) appContext; HabitsApplication app = (HabitsApplication) appContext;
taskRunner = app.getComponent().getTaskRunner();
prefs = app.getComponent().getPreferences(); prefs = app.getComponent().getPreferences();
} }
@ -140,11 +129,18 @@ public class ScoreCard extends HabitCard
bucketSize = BUCKET_SIZES[position]; bucketSize = BUCKET_SIZES[position];
} }
private class RefreshTask implements Task @Override
protected Task createRefreshTask()
{
return new RefreshTask();
}
private class RefreshTask extends CancelableTask
{ {
@Override @Override
public void doInBackground() public void doInBackground()
{ {
if (isCanceled()) return;
List<Score> scores; List<Score> scores;
ScoreList scoreList = getHabit().getScores(); ScoreList scoreList = getHabit().getScores();

@ -45,9 +45,6 @@ public class StreakCard extends HabitCard
@BindView(R.id.streakChart) @BindView(R.id.streakChart)
StreakChart streakChart; StreakChart streakChart;
@Nullable
private TaskRunner taskRunner;
public StreakCard(Context context) public StreakCard(Context context)
{ {
super(context); super(context);
@ -60,22 +57,8 @@ public class StreakCard extends HabitCard
init(); init();
} }
@Override
protected void refreshData()
{
if(taskRunner == null) return;
taskRunner.execute(new RefreshTask());
}
private void init() private void init()
{ {
Context appContext = getContext().getApplicationContext();
if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
taskRunner = app.getComponent().getTaskRunner();
}
inflate(getContext(), R.layout.show_habit_streak, this); inflate(getContext(), R.layout.show_habit_streak, this);
ButterKnife.bind(this); ButterKnife.bind(this);
setOrientation(VERTICAL); setOrientation(VERTICAL);
@ -90,13 +73,20 @@ public class StreakCard extends HabitCard
streakChart.populateWithRandomData(); streakChart.populateWithRandomData();
} }
private class RefreshTask implements Task @Override
protected Task createRefreshTask()
{
return new RefreshTask();
}
private class RefreshTask extends CancelableTask
{ {
public List<Streak> bestStreaks; List<Streak> bestStreaks;
@Override @Override
public void doInBackground() public void doInBackground()
{ {
if (isCanceled()) return;
StreakList streaks = getHabit().getStreaks(); StreakList streaks = getHabit().getStreaks();
bestStreaks = streaks.getBest(NUM_STREAKS); bestStreaks = streaks.getBest(NUM_STREAKS);
} }
@ -104,6 +94,7 @@ public class StreakCard extends HabitCard
@Override @Override
public void onPostExecute() public void onPostExecute()
{ {
if (isCanceled()) return;
streakChart.setStreaks(bestStreaks); streakChart.setStreaks(bestStreaks);
} }

@ -27,6 +27,7 @@ import android.widget.*;
import org.isoron.uhabits.R; import org.isoron.uhabits.R;
import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import butterknife.*; import butterknife.*;
@ -111,4 +112,10 @@ public class SubtitleCard extends HabitCard
AndroidDateUtils.formatTime(getContext(), reminder.getHour(), AndroidDateUtils.formatTime(getContext(), reminder.getHour(),
reminder.getMinute())); reminder.getMinute()));
} }
@Override
protected Task createRefreshTask() {
// Never called
throw new IllegalStateException();
}
} }

@ -87,6 +87,7 @@ public class AndroidTaskRunner implements TaskRunner
private class CustomAsyncTask extends AsyncTask<Void, Integer, Void> private class CustomAsyncTask extends AsyncTask<Void, Integer, Void>
{ {
private final Task task; private final Task task;
private boolean isCancelled = false;
public CustomAsyncTask(Task task) public CustomAsyncTask(Task task)
{ {
@ -106,6 +107,7 @@ public class AndroidTaskRunner implements TaskRunner
@Override @Override
protected Void doInBackground(Void... params) protected Void doInBackground(Void... params)
{ {
if(isCancelled) return null;
task.doInBackground(); task.doInBackground();
return null; return null;
} }
@ -113,6 +115,7 @@ public class AndroidTaskRunner implements TaskRunner
@Override @Override
protected void onPostExecute(Void aVoid) protected void onPostExecute(Void aVoid)
{ {
if(isCancelled) return;
task.onPostExecute(); task.onPostExecute();
activeTasks.remove(this); activeTasks.remove(this);
taskToAsyncTask.remove(task); taskToAsyncTask.remove(task);
@ -122,6 +125,8 @@ public class AndroidTaskRunner implements TaskRunner
@Override @Override
protected void onPreExecute() protected void onPreExecute()
{ {
isCancelled = task.isCanceled();
if(isCancelled) return;
for (Listener l : listeners) l.onTaskStarted(task); for (Listener l : listeners) l.onTaskStarted(task);
activeTasks.add(this); activeTasks.add(this);
taskToAsyncTask.put(task, this); taskToAsyncTask.put(task, this);

@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.tasks;
public abstract class CancelableTask implements Task
{
private boolean isCanceled = false;
@Override
public synchronized void cancel()
{
isCanceled = true;
}
@Override
public synchronized boolean isCanceled()
{
return isCanceled;
}
}

@ -35,10 +35,13 @@ public class SingleThreadTaskRunner implements TaskRunner
public void execute(Task task) public void execute(Task task)
{ {
for(Listener l : listeners) l.onTaskStarted(task); for(Listener l : listeners) l.onTaskStarted(task);
if(!task.isCanceled())
{
task.onAttached(this); task.onAttached(this);
task.onPreExecute(); task.onPreExecute();
task.doInBackground(); task.doInBackground();
task.onPostExecute(); task.onPostExecute();
}
for(Listener l : listeners) l.onTaskFinished(task); for(Listener l : listeners) l.onTaskFinished(task);
} }

@ -25,6 +25,11 @@ public interface Task
{ {
default void cancel() {} default void cancel() {}
default boolean isCanceled()
{
return false;
}
void doInBackground(); void doInBackground();
default void onAttached(@NonNull TaskRunner runner) {} default void onAttached(@NonNull TaskRunner runner) {}

Loading…
Cancel
Save