测试我的DB时,我会收到以下错误:
- sqlitediskioException:磁盘I/O错误(HTC Desire 620)
- sqlitereadonlydatabaseexception:尝试编写只读数据库(Moto G2)
显然取决于我对其进行测试的设备。我运行应用程序时不会发生错误。不过,如果我无法测试该应用程序,我的代码可能有问题。
该应用程序使用两个应该很好地融合在一起的库和sqlbrite,这可能会使这个问题有些具体。
为了更好地了解正在发生的事情,我将简要描述我的数据包中的文件。
-+-data-+
| |-manager-+
| | |-LocationManager
| | |-RunManager
| |-model-+
| | |-Location
| | |-Run
|-DatabaseContract
|-DataRepository
|-MyDBHelper
文件Location
和Run
是由SQLDELIGHT生成的行模型。LocationManager
和RunManager
启用生成SQLStatement以插入或从相应表中删除数据。在RunManager
下方LocationMangager
看起来相似。
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);
}
}
mydbhelper以标准方式扩展了sqliteopenhelper。
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)
}
}
数据存储库汇总了各种插入和查询操作。
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
}
现在到了问题的主要部分。目前,我编写了以下测试用例,并遇到了顶部列出的错误。有趣的是,当我分别进行测试时,我没有任何错误,所有测试都通过了。
@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());
}
}
我认为这与从不同线程访问DB有关,但我不知道该如何解决问题。
您的问题是,当setUp
第二次运行时,DataRepository.getInstance
返回 old> old 数据存储库,这意味着它不会创建新的SQLiteOpenHelper
。删除数据库时,您还需要清理DataRepository和MyDBhelper的单例。
或者完全不使用单例:
@Before
public void setUp() {
context = InstrumentationRegistry.getTargetContext();
context.deleteDatabase(MyDbHelper.DATABASE_NAME);
mDataRepository = new DataRepository(InstrumentationRegistry.getTargetContext());
}
// In DataRepository.java
DataRepository(Context context) {
SqlBrite sqlBrite = new SqlBrite.Builder().build();
MyDbHelper helper = new MyDbHelper(context);
briteDatabase = sqlBrite.wrapDatabaseHelper(helper, Schedulers.io());
locationManger = new LocationManger(briteDatabase.getWritableDatabase());
runManager = new RunManager(briteDatabase.getWritableDatabase());
}
// In MyDbHelper.java
MyDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}