Als meine DB Prüfung erhalte ich folgende Fehler:Fehler beim Testen Datenbankrepository - SQLBrite, SQLDelight
- SQLiteDiskIOException: Festplatten-E/A-Fehler (HTC Desire 620)
- SQLiteReadOnlyDatabaseException: Versuch, eine Lese zu schreiben -only Datenbank (Moto g2)
Abhängig anscheinend auf dem Gerät teste ich es an. Der Fehler tritt nicht auf, wenn ich die App starte. Trotzdem, wenn ich die App nicht testen kann, ist wahrscheinlich etwas mit meinem Code nicht in Ordnung.
Die App verwendet zwei Bibliotheken, die SQLDelight und SQLBrite gut zusammenpassen sollen, was diese Frage etwas spezifisch machen könnte.
Für ein besseres Verständnis der Vorgänge werde ich eine kurze Beschreibung der Dateien in meinem Datenpaket geben.
-+-data-+
| |-manager-+
| | |-LocationManager
| | |-RunManager
| |-model-+
| | |-Location
| | |-Run
|-DatabaseContract
|-DataRepository
|-MyDBHelper
Die Dateien Location
und Run
sind Reihe Modelle von SQLDelight erzeugt. LocationManager
und RunManager
aktivieren Sie das Generieren von sqlStatements zum Einfügen oder Entfernen von Daten aus der entsprechenden Tabelle. Unter der RunManager
sieht die LocationMangager
ähnlich aus.
public class RunManager {
public final Run.InsertRun insertRace;
public final Run.DeleteRun deleteRun;
public final Run.DeleteRunWhereTimeSmallerThan deleteRunWhereTimeSmallerThan;
public RunManager(SQLiteDatabase db) {
insertRace = new Run.InsertRun(db);
deleteRun = new Run.DeleteRun(db);
deleteRunWhereTimeSmallerThan = new Run.DeleteRunWhereTimeSmallerThan(db);
}
}
Der MyDBHelper erweitert den SQLiteOpenHelper auf eine standardmäßige Weise.
public class MyDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "runner.db";
private static MyDbHelper INSTANCE = null;
private MyDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public static MyDbHelper getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = new MyDbHelper(context);
}
return INSTANCE;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// create table (omitted)
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
// upgrade table (omitted)
}
}
Das Datenrepository fasst die verschiedenen Einfüge- und Abfrageoperationen zusammen.
public class DataRepository implements DatabaseContract {
private static DataRepository INSTANCE = null;
BriteDatabase briteDatabase;
LocationManger locationManger;
RunManager runManager;
private DataRepository(Context context) {
SqlBrite sqlBrite = new SqlBrite.Builder().build();
MyDbHelper helper = MyDbHelper.getInstance(context);
briteDatabase = sqlBrite.wrapDatabaseHelper(helper, Schedulers.io());
locationManger = new LocationManger(briteDatabase.getWritableDatabase());
runManager = new RunManager(briteDatabase.getWritableDatabase());
}
public static DataRepository getInstance(Context context) {
if (null == INSTANCE) {
INSTANCE = new DataRepository(context);
}
return INSTANCE;
}
@Override
public Observable<List<Run>> getAllRun() {
return briteDatabase.createQuery(
Run.TABLE_NAME,
Run.SELECT_ALL
).mapToList(Run.MAPPER::map);
}
@Override
public Observable<List<Location>> getLocationsForRun(long id) {
return briteDatabase.createQuery(
Location.TABLE_NAME, Location.FACTORY.selectAllByRace(id).statement
).mapToList(Location.MAPPER::map);
}
@Override
public long insertRun(double distance, long duration, double avgSpeed, long timestamp) {
runManager.insertRace.bind(distance, duration, avgSpeed, timestamp);
return briteDatabase.executeInsert(Run.TABLE_NAME, runManager.insertRace.program);
}
@Override
public void deleteRun(long id) {
runManager.deleteRun.bind(id);
briteDatabase.executeUpdateDelete(Run.TABLE_NAME, runManager.deleteRun.program);
}
@Override
public void deleteRunWhereTimestampSmallerThan(long timestamp) {
runManager.deleteRunWhereTimeSmallerThan.bind(timestamp);
briteDatabase.executeUpdateDelete(Run.TABLE_NAME, runManager.deleteRunWhereTimeSmallerThan.program);
}
@Override
public long insertLocation(long raceId, double lat, double lng, double alt, long timestamp) {
locationManger.insertLocation.bind(raceId, lat, lng, alt, timestamp);
return briteDatabase.executeInsert(Location.TABLE_NAME, locationManger.insertLocation.program);
}
public Observable<List<SingleRun>> getAllSingleRunModels() {
return briteDatabase.createQuery(
Run.TABLE_NAME,
Run.SELECT_ALL
).mapToList(Run.MAPPER::map)
// omitted
}
Nun zum Hauptteil der Frage. Für den Moment habe ich die folgenden Testfälle geschrieben und führe die oben aufgelisteten Fehler durch. Interessanterweise, wenn ich die Tests getrennt durchführe, bekomme ich keine Fehler, alle Tests bestanden.
@RunWith(AndroidJUnit4.class)
@LargeTest
public class DataRepositoryTest {
private Context context;
private DataRepository mDataRepository;
@Before
public void setUp() {
context = InstrumentationRegistry.getTargetContext();
context.deleteDatabase(MyDbHelper.DATABASE_NAME);
mDataRepository = DataRepository.getInstance(InstrumentationRegistry.getTargetContext());
}
@Test
public void testPreConditions() {
Assert.assertNotNull(context);
Assert.assertNotNull(mDataRepository);
}
@Test
public void testInsertRace() { // this test failes when all tests are run.
long raceID1 = mDataRepository.insertRun(5.0, 35, 3.5, 1000);
Assert.assertEquals(1, raceID1);
long raceID2 = mDataRepository.insertRun(10.0, 70, 3.5, 2000);
Assert.assertEquals(2, raceID2);
long locationID1 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1000);
Assert.assertEquals(1, locationID1);
long locationID2 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1001);
Assert.assertEquals(2, locationID2);
long locationID3 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1002);
Assert.assertEquals(3, locationID3);
long locationID4 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1003);
Assert.assertEquals(4, locationID4);
long locationID5 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2000);
Assert.assertEquals(5, locationID5);
long locationID6 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2001);
Assert.assertEquals(6, locationID6);
long locationID7 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2002);
Assert.assertEquals(7, locationID7);
long locationID8 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2003);
Assert.assertEquals(8, locationID8);
}
@Test
public void testRaceObservable() {
long raceID1 = mDataRepository.insertRun(5.0, 35, 3.5, 1000);
Run run1 = Run.FACTORY.creator.create(raceID1, 5.0, 35l, 3.5, 1000l);
Assert.assertEquals(1, raceID1);
long raceID2 = mDataRepository.insertRun(10.0, 70, 3.5, 2000);
Run run2 = Run.FACTORY.creator.create(raceID2, 10.0, 70l, 3.5, 2000l);
Assert.assertEquals(2, raceID2);
List<Run> expectedResult = Arrays.asList(run1, run2);
Assert.assertEquals(expectedResult, mDataRepository.getAllRun().blockingFirst());
}
}
Ich gehe davon aus, das hat etwas mit dem Zugriff auf die DB von verschiedenen Threads zu tun, aber ich weiß nicht, wie das Problem zu lösen.