我正在自学内容提供商的基础知识。
我有一个名为 BooksProvder 的 ContentProvider 合约类。它的基础数据位于名为“fiction”的 SQLite 数据库中,其中包含列 _ID、_TITlE、_AUTHOR 和 _YEAR。
在提供商的应用程序中,我还有:
在图书提供商中:
cursor = dbHelper.db.query(BooksContract.TABLE, null, null, null,null,null,null);
但是当我尝试从测试应用程序访问提供程序时,上面的行抛出 NullPointerException,表明 db 为空。 db 不应该为空,因为我已经从 AVD 的设备文件资源管理器中找到并打开了它。我不明白为什么它是空的。欢迎任何帮助。
相关代码为:
清单:
<application
<provider android:authorities="com.mo.books_provider_app"
android:name=".BooksProvider"
android:enabled="true"
android:exported="true">
</provider>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
DBHelper类:
public class DBHelper extends SQLiteOpenHelper {
SQLiteDatabase db;
Context context;
public DBHelper(@Nullable Context context) {
super(context, null, null, 1);
this.context = context;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// Check if database already exists in the app's specific directory
File f = context.getDatabasePath("Books.db");
if (f.exists()) {
db = getWritableDatabase();
} else {
AssetCopier assetCopier = new AssetCopier(context, "Books.db", "Books.db");
try {
assetCopier.copyFromAssets("Books.db");
} catch (IOException e) {
throw new RuntimeException(e.getCause());
}
}
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
// do nothing - there will only be one version (schema of the database)
}
}
BooksProvder 类的相关片段:
public class BooksProvider extends ContentProvider {
DBHelper dbHelper;
Context context;
UriMatcher uriMatcher;
Cursor cursor;
@Override
public boolean onCreate() {
this.context = getContext();
dbHelper = new DBHelper(context);
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); // the uri of the whole content provider itself
uriMatcher.addURI(BooksContract.AUTHORITY, BooksContract.PATH, BooksContract.TABLE_MATCH_CODE);
uriMatcher.addURI(BooksContract.AUTHORITY, BooksContract.PATH + "/*", BooksContract.ROW_MATCH_CODE);
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri,
@Nullable String[] projection,
@Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable String sortOrder) {
// query the uri pattern
switch (uriMatcher.match(uri)) {
case BooksContract.TABLE_MATCH_CODE: // Return the whole table, all rows, all columns
cursor = dbHelper.db.query(BooksContract.TABLE,
null, null, null,null,null,null);
break;
case BooksContract.ROW_MATCH_CODE: // Return a particular row
// Extract the row ID (row number from the uri)
String bookId = uri.getLastPathSegment();
cursor = dbHelper.db.query(BooksContract.TABLE, projection, "_ID = ?", new String[] {bookId},
null, null, null);
break;
default:
throw new IllegalArgumentException ("Unkown URI : + uri");
}
return cursor;
}
如果 books.db 不存在,那么您不会实例化 db,因此它为 null 并生成 NPE。
成功复制资产后,您需要将 db 实例化为 SQLiteDatabase,这将要求 books.db 作为 SQLiteDatabase 打开。
因此,在复制资产后,将执行以下操作:-
} else {
// AssetCopier code not supplied in question so mimic the copy
//AssetCopier assetCopier = new AssetCopier(context, "Books.db", "Books.db");
// ....
db = SQLiteDatabase.openDatabase(f.getPath(),null,SQLiteDatabase.OPEN_READWRITE); /* Open the database which now exists (at least should do it AssetCopier did it's business) */
}
当涉及到现有文件时,您可能想(再次)使用而不是获取内存中的文件(即不是Books.db的临时空数据库)
db = SQLiteDatabase.openDatabase(f.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
而不是
db = getWritableDatabase();
sqliteDatabase,由于超级调用的数据库名称参数为null,因此将是内存数据库。
确保始终使用未持久化的内存数据库来调用 onCreate 是处理资产副本或不复制的新颖/不寻常的方式。