Improve performance of toggleCheckmark

pull/77/merge
Alinson S. Xavier 10 years ago
parent 5763d76167
commit bc54f3f916

@ -25,6 +25,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import org.isoron.uhabits.BaseTest;
import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Repetition;
import org.isoron.uhabits.unit.HabitFixtures;
import org.junit.After;
import org.junit.Before;
@ -37,6 +38,7 @@ import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Random;
import static junit.framework.Assert.assertFalse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
@ -174,4 +176,16 @@ public class RepetitionListTest extends BaseTest
from = to - 5 * DateHelper.millisecondsInOneDay;
assertThat(habit.repetitions.count(from, to), equalTo(3));
}
@Test
public void getOldest()
{
long expectedOldestTimestamp = DateHelper.getStartOfToday() - 9 * DateHelper.millisecondsInOneDay;
assertThat(habit.repetitions.getOldestTimestamp(), equalTo(expectedOldestTimestamp));
Repetition oldest = habit.repetitions.getOldest();
assertFalse(oldest == null);
assertThat(oldest.timestamp, equalTo(expectedOldestTimestamp));
}
}

@ -20,11 +20,13 @@
package org.isoron.uhabits.helpers;
import android.content.Context;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import com.activeandroid.ActiveAndroid;
import com.activeandroid.Cache;
import com.activeandroid.Configuration;
import org.isoron.uhabits.BuildConfig;
@ -163,4 +165,20 @@ public class DatabaseHelper
ActiveAndroid.initialize(dbConfig);
}
public static long longQuery(String query, String args[])
{
Cursor c = null;
try
{
c = Cache.openDatabase().rawQuery(query, args);
if (!c.moveToFirst()) return 0;
return c.getLong(0);
}
finally
{
if(c != null) c.close();
}
}
}

@ -21,10 +21,10 @@ package org.isoron.uhabits.models;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.activeandroid.ActiveAndroid;
import com.activeandroid.Cache;
import com.activeandroid.query.Delete;
import com.activeandroid.query.Select;
@ -134,10 +134,9 @@ public class CheckmarkList
*/
protected void computeAll()
{
Repetition oldestRep = habit.repetitions.getOldest();
if(oldestRep == null) return;
long fromTimestamp = habit.repetitions.getOldestTimestamp();
if(fromTimestamp == 0) return;
Long fromTimestamp = oldestRep.timestamp;
Long toTimestamp = DateHelper.getStartOfToday();
compute(fromTimestamp, toTimestamp);
@ -150,9 +149,9 @@ public class CheckmarkList
* @param from timestamp for the beginning of the interval
* @param to timestamp for the end of the interval
*/
protected void compute(long from, long to)
protected void compute(long from, final long to)
{
long day = DateHelper.millisecondsInOneDay;
final long day = DateHelper.millisecondsInOneDay;
Checkmark newestCheckmark = findNewest();
if(newestCheckmark != null)
@ -165,9 +164,9 @@ public class CheckmarkList
.selectFromTo(fromExtended, to)
.execute();
int nDays = (int) ((to - from) / day) + 1;
final int nDays = (int) ((to - from) / day) + 1;
int nDaysExtended = (int) ((to - fromExtended) / day) + 1;
int checks[] = new int[nDaysExtended];
final int checks[] = new int[nDaysExtended];
for (Repetition rep : reps)
{
@ -187,24 +186,38 @@ public class CheckmarkList
checks[i] = Checkmark.CHECKED_IMPLICITLY;
}
ActiveAndroid.beginTransaction();
long timestamps[] = new long[nDays];
for (int i = 0; i < nDays; i++)
timestamps[i] = to - i * day;
insert(timestamps, checks);
}
private void insert(long timestamps[], int values[])
{
String query = "insert into Checkmarks(habit, timestamp, value) values (?,?,?)";
SQLiteDatabase db = Cache.openDatabase();
db.beginTransaction();
try
{
for (int i = 0; i < nDays; i++)
SQLiteStatement statement = db.compileStatement(query);
statement.bindString(1, habit.getId().toString());
for (int i = 0; i < timestamps.length; i++)
{
Checkmark c = new Checkmark();
c.habit = habit;
c.timestamp = to - i * day;
c.value = checks[i];
c.save();
statement.bindLong(2, timestamps[i]);
statement.bindLong(3, values[i]);
statement.execute();
}
ActiveAndroid.setTransactionSuccessful();
db.setTransactionSuccessful();
}
finally
{
ActiveAndroid.endTransaction();
db.endTransaction();
}
}

@ -28,7 +28,9 @@ import com.activeandroid.Cache;
import com.activeandroid.query.Delete;
import com.activeandroid.query.From;
import com.activeandroid.query.Select;
import com.activeandroid.util.SQLiteUtils;
import org.isoron.uhabits.helpers.DatabaseHelper;
import org.isoron.uhabits.helpers.DateHelper;
import java.util.Arrays;
@ -96,22 +98,21 @@ public class RepetitionList
timestamp = DateHelper.getStartOfDay(timestamp);
if (contains(timestamp))
{
delete(timestamp);
}
else
{
Repetition rep = new Repetition();
rep.habit = habit;
rep.timestamp = timestamp;
rep.save();
}
insert(timestamp);
habit.scores.invalidateNewerThan(timestamp);
habit.checkmarks.deleteNewerThan(timestamp);
habit.streaks.deleteNewerThan(timestamp);
}
private void insert(long timestamp)
{
String[] args = { habit.getId().toString(), Long.toString(timestamp) };
SQLiteUtils.execSql("insert into Repetitions(habit, timestamp) values (?,?)", args);
}
/**
* Returns the oldest repetition for the habit. If there is no repetition, returns null.
* Repetitions in the future are discarded.
@ -124,6 +125,21 @@ public class RepetitionList
return (Repetition) select().limit(1).executeSingle();
}
/**
* Returns the timestamp of the oldest repetition. If there are no repetitions, returns zero.
* Repetitions in the future are discarded.
*
* @return timestamp of the oldest repetition
*/
public long getOldestTimestamp()
{
String[] args = { habit.getId().toString(), Long.toString(DateHelper.getStartOfToday()) };
String query = "select timestamp from Repetitions where habit = ? and timestamp <= ? " +
"order by timestamp limit 1";
return DatabaseHelper.longQuery(query, args);
}
/**
* Returns the total number of repetitions for each month, from the first repetition until
* today, grouped by day of week. The repetitions are returned in a HashMap. The key is the

@ -21,6 +21,7 @@ package org.isoron.uhabits.models;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -28,8 +29,8 @@ import com.activeandroid.Cache;
import com.activeandroid.query.Delete;
import com.activeandroid.query.From;
import com.activeandroid.query.Select;
import com.activeandroid.util.SQLiteUtils;
import org.isoron.uhabits.helpers.DatabaseHelper;
import org.isoron.uhabits.helpers.DateHelper;
import java.io.IOException;
@ -104,10 +105,9 @@ public class ScoreList
*/
private void computeAll()
{
Repetition oldestRep = habit.repetitions.getOldest();
if(oldestRep == null) return;
long fromTimestamp = habit.repetitions.getOldestTimestamp();
if(fromTimestamp == 0) return;
long fromTimestamp = oldestRep.timestamp;
long toTimestamp = DateHelper.getStartOfToday();
compute(fromTimestamp, toTimestamp);
}
@ -136,28 +136,51 @@ public class ScoreList
from = newestScore.timestamp + day;
final int checkmarkValues[] = habit.checkmarks.getValues(from, to);
final int firstScore = newestScoreValue;
final long beginning = from;
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
int lastScore = newestScoreValue;
int size = checkmarkValues.length;
long timestamps[] = new long[size];
long values[] = new long[size];
for (int i = 0; i < checkmarkValues.length; i++)
{
int checkmarkValue = checkmarkValues[checkmarkValues.length - i - 1];
lastScore = Score.compute(freq, lastScore, checkmarkValue);
timestamps[i] = beginning + day * i;
values[i] = lastScore;
}
insert(timestamps, values);
}
private void insert(long timestamps[], long values[])
{
String query = "insert into Score(habit, timestamp, score) values (?,?,?)";
SQLiteDatabase db = Cache.openDatabase();
db.beginTransaction();
try
{
@Override
public void execute()
SQLiteStatement statement = db.compileStatement(query);
statement.bindString(1, habit.getId().toString());
for (int i = 0; i < timestamps.length; i++)
{
int lastScore = firstScore;
for (int i = 0; i < checkmarkValues.length; i++)
{
int checkmarkValue = checkmarkValues[checkmarkValues.length - i - 1];
Score s = new Score();
s.habit = habit;
s.timestamp = beginning + day * i;
s.score = lastScore = Score.compute(freq, lastScore, checkmarkValue);
s.save();
}
statement.bindLong(2, timestamps[i]);
statement.bindLong(3, values[i]);
statement.execute();
}
});
db.setTransactionSuccessful();
}
finally
{
db.endTransaction();
}
}
/**
@ -185,9 +208,9 @@ public class ScoreList
*/
public int getValue(long timestamp)
{
Score s = get(timestamp);
if(s == null) return 0;
else return s.score;
computeAll();
String[] args = { habit.getId().toString(), Long.toString(timestamp) };
return SQLiteUtils.intQuery("select score from Score where habit = ? and timestamp = ?", args);
}
/**

Loading…
Cancel
Save