diff --git a/.gitignore b/.gitignore index e575874af..cce506928 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ docs/ gen/ local.properties crowdin.yaml +local diff --git a/app/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java b/app/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java new file mode 100644 index 000000000..765c4cd3a --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.performance; + +import android.support.test.filters.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.junit.*; + +@MediumTest +public class PerformanceTest extends BaseAndroidTest +{ + private Habit habit; + + @Override + public void setUp() + { + super.setUp(); + habit = fixtures.createLongHabit(); + } + + @Test(timeout = 1000) + public void testRepeatedGetTodayValue() + { + for (int i = 0; i < 100000; i++) + { + habit.getScores().getTodayValue(); + habit.getCheckmarks().getTodayValue(); + } + } + +} diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java index 7304a16bf..b71625295 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java @@ -37,6 +37,7 @@ import org.isoron.uhabits.preferences.*; import org.isoron.uhabits.utils.*; import org.junit.*; +import java.io.*; import java.util.*; import java.util.concurrent.*; @@ -162,4 +163,18 @@ public class BaseAndroidTest extends TestCase cal.set(year, month, day); return cal.getTimeInMillis(); } + + protected void startTracing() + { + File dir = baseSystem.getFilesDir("Profile"); + assertNotNull(dir); + String tracePath = dir.getAbsolutePath() + "/performance.trace"; + Log.d("PerformanceTest", String.format("Saving trace file to %s", tracePath)); + Debug.startMethodTracingSampling(tracePath, 0, 1000); + } + + protected void stopTracing() + { + Debug.stopMethodTracing(); + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java index a532e1018..74e2f73a9 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java @@ -24,7 +24,6 @@ import android.support.annotation.*; import android.support.annotation.Nullable; import com.activeandroid.*; -import com.activeandroid.query.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.utils.*; @@ -38,38 +37,54 @@ import java.util.*; */ public class SQLiteCheckmarkList extends CheckmarkList { + @Nullable private HabitRecord habitRecord; @NonNull private final SQLiteUtils sqlite; + @Nullable + private Integer todayValue; + + @NonNull + private final SQLiteStatement invalidateStatement; + + @NonNull + private final SQLiteStatement addStatement; + + @NonNull + private final SQLiteDatabase db; + + private static final String ADD_QUERY = + "insert into Checkmarks(habit, timestamp, value) values (?,?,?)"; + + private static final String INVALIDATE_QUERY = + "delete from Checkmarks where habit = ? and timestamp >= ?"; + public SQLiteCheckmarkList(Habit habit) { super(habit); sqlite = new SQLiteUtils<>(CheckmarkRecord.class); + + db = Cache.openDatabase(); + addStatement = db.compileStatement(ADD_QUERY); + invalidateStatement = db.compileStatement(INVALIDATE_QUERY); } @Override public void add(List checkmarks) { check(habit.getId()); - - String query = - "insert into Checkmarks(habit, timestamp, value) values (?,?,?)"; - - SQLiteDatabase db = Cache.openDatabase(); db.beginTransaction(); try { - SQLiteStatement statement = db.compileStatement(query); - for (Checkmark c : checkmarks) { - statement.bindLong(1, habit.getId()); - statement.bindLong(2, c.getTimestamp()); - statement.bindLong(3, c.getValue()); - statement.execute(); + addStatement.bindLong(1, habit.getId()); + addStatement.bindLong(2, c.getTimestamp()); + addStatement.bindLong(3, c.getValue()); + addStatement.execute(); } db.setTransactionSuccessful(); @@ -115,12 +130,10 @@ public class SQLiteCheckmarkList extends CheckmarkList @Override public void invalidateNewerThan(long timestamp) { - new Delete() - .from(CheckmarkRecord.class) - .where("habit = ?", habit.getId()) - .and("timestamp >= ?", timestamp) - .execute(); - + todayValue = null; + invalidateStatement.bindLong(1, habit.getId()); + invalidateStatement.bindLong(2, timestamp); + invalidateStatement.execute(); observable.notifyListeners(); } @@ -179,4 +192,11 @@ public class SQLiteCheckmarkList extends CheckmarkList for (CheckmarkRecord r : records) checkmarks.add(r.toCheckmark()); return checkmarks; } + + @Override + public int getTodayValue() + { + if(todayValue == null) todayValue = super.getTodayValue(); + return todayValue; + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java index 7641b356d..0a4969cd4 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java @@ -43,10 +43,16 @@ public class SQLiteRepetitionList extends RepetitionList @Nullable private HabitRecord habitRecord; + private SQLiteStatement addStatement; + public SQLiteRepetitionList(@NonNull Habit habit) { super(habit); sqlite = new SQLiteUtils<>(RepetitionRecord.class); + + SQLiteDatabase db = Cache.openDatabase(); + String addQuery = "insert into repetitions(habit, timestamp) values (?,?)"; + addStatement = db.compileStatement(addQuery); } /** @@ -61,11 +67,9 @@ public class SQLiteRepetitionList extends RepetitionList public void add(Repetition rep) { check(habit.getId()); - - RepetitionRecord record = new RepetitionRecord(); - record.copyFrom(rep); - record.habit = habitRecord; - record.save(); + addStatement.bindLong(1, habit.getId()); + addStatement.bindLong(2, rep.getTimestamp()); + addStatement.execute(); observable.notifyListeners(); } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java index 4474db0b0..02e082cae 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java @@ -24,7 +24,6 @@ import android.support.annotation.*; import android.support.annotation.Nullable; import com.activeandroid.*; -import com.activeandroid.query.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.models.sqlite.records.*; @@ -37,12 +36,30 @@ import java.util.*; */ public class SQLiteScoreList extends ScoreList { + @Nullable private HabitRecord habitRecord; @NonNull private final SQLiteUtils sqlite; + @Nullable + private Double todayValue; + + @NonNull + private final SQLiteStatement invalidateStatement; + + @NonNull + private final SQLiteStatement addStatement; + + public static final String ADD_QUERY = + "insert into Score(habit, timestamp, score) values (?,?,?)"; + + public static final String INVALIDATE_QUERY = + "delete from Score where habit = ? " + "and timestamp >= ?"; + + private final SQLiteDatabase db; + /** * Constructs a new ScoreList associated with the given habit. * @@ -52,28 +69,25 @@ public class SQLiteScoreList extends ScoreList { super(habit); sqlite = new SQLiteUtils<>(ScoreRecord.class); + + db = Cache.openDatabase(); + addStatement = db.compileStatement(ADD_QUERY); + invalidateStatement = db.compileStatement(INVALIDATE_QUERY); } @Override public void add(List scores) { check(habit.getId()); - String query = - "insert into Score(habit, timestamp, score) values (?,?,?)"; - - SQLiteDatabase db = Cache.openDatabase(); db.beginTransaction(); - try { - SQLiteStatement statement = db.compileStatement(query); - for (Score s : scores) { - statement.bindLong(1, habit.getId()); - statement.bindLong(2, s.getTimestamp()); - statement.bindDouble(3, s.getValue()); - statement.execute(); + addStatement.bindLong(1, habit.getId()); + addStatement.bindLong(2, s.getTimestamp()); + addStatement.bindDouble(3, s.getValue()); + addStatement.execute(); } db.setTransactionSuccessful(); @@ -126,13 +140,10 @@ public class SQLiteScoreList extends ScoreList @Override public void invalidateNewerThan(long timestamp) { - new Delete() - .from(ScoreRecord.class) - .where("habit = ?", habit.getId()) - .and("timestamp >= ?", timestamp) - .execute(); - todayValue = null; + invalidateStatement.bindLong(1, habit.getId()); + invalidateStatement.bindLong(2, timestamp); + invalidateStatement.execute(); getObservable().notifyListeners(); } @@ -205,4 +216,11 @@ public class SQLiteScoreList extends ScoreList for (ScoreRecord r : records) scores.add(r.toScore()); return scores; } + + @Override + public double getTodayValue() + { + if (todayValue == null) todayValue = super.getTodayValue(); + return todayValue; + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java index 43a6e0e9d..168b7313a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java @@ -19,10 +19,11 @@ package org.isoron.uhabits.models.sqlite; +import android.database.sqlite.*; import android.support.annotation.*; import android.support.annotation.Nullable; -import com.activeandroid.query.*; +import com.activeandroid.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.utils.*; @@ -42,10 +43,17 @@ public class SQLiteStreakList extends StreakList @NonNull private final SQLiteUtils sqlite; + private final SQLiteStatement invalidateStatement; + public SQLiteStreakList(Habit habit) { super(habit); sqlite = new SQLiteUtils<>(StreakRecord.class); + + SQLiteDatabase db = Cache.openDatabase(); + String invalidateQuery = "delete from Streak where habit = ? " + + "and end >= ?"; + invalidateStatement = db.compileStatement(invalidateQuery); } @Override @@ -74,12 +82,9 @@ public class SQLiteStreakList extends StreakList @Override public void invalidateNewerThan(long timestamp) { - new Delete() - .from(StreakRecord.class) - .where("habit = ?", habit.getId()) - .and("end >= ?", timestamp - DateUtils.millisecondsInOneDay) - .execute(); - + invalidateStatement.bindLong(1, habit.getId()); + invalidateStatement.bindLong(2, timestamp - DateUtils.millisecondsInOneDay); + invalidateStatement.execute(); observable.notifyListeners(); } diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java index 008191611..75a34e42b 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java @@ -113,7 +113,7 @@ public abstract class CheckmarkList * * @return value of today's checkmark */ - public synchronized final int getTodayValue() + public synchronized int getTodayValue() { Checkmark today = getToday(); if (today != null) return today.getValue();