访问自定义Content Provider数据遇阻,附BirthProvider实现代码求助
排查并修复BirthProvider访问故障的方案
先看你提供的代码,有几个明显的问题导致Content Provider无法正常工作,我一步步帮你梳理和修复:
1. DBHelper类代码不完整(致命问题)
你的代码里DBHelper类只写了一半就截断了,这会导致数据库根本无法创建。补全完整的DBHelper实现:
// class that creates and manages the provider's database private static class DBHelper extends SQLiteOpenHelper { public DBHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { // 执行建表语句 db.execSQL(CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 简单升级逻辑(实际项目建议做数据迁移,不要直接删表) db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } }
2. BirthMap投影映射未初始化
你在query方法里用了BirthMap,但这个HashMap完全没赋值,会导致查询时列名映射错误,甚至空指针。在static块里初始化它:
static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "friends", FRIENDS); uriMatcher.addURI(PROVIDER_NAME, "friends/#", FRIENDS_ID); // 初始化投影映射,确保查询时列名正确对应 BirthMap = new HashMap<>(); BirthMap.put(ID, ID); BirthMap.put(NAME, NAME); BirthMap.put(BIRTHDAY, BIRTHDAY); }
3. MIME类型定义不符合规范
getType方法里的MIME类型用了vnd.example.friends,但你的PROVIDER_NAME是com.mprovider.BirthdayDude,两者要统一,否则ContentResolver无法正确识别数据类型:
@Nullable @Override public String getType(@NonNull Uri uri) { switch (uriMatcher.match(uri)) { // 多条目MIME类型格式:vnd.android.cursor.dir/vnd.<provider-authority>.<path> case FRIENDS: return "vnd.android.cursor.dir/vnd." + PROVIDER_NAME + ".friends"; // 单条目MIME类型格式:vnd.android.cursor.item/vnd.<provider-authority>.<path> case FRIENDS_ID: return "vnd.android.cursor.item/vnd." + PROVIDER_NAME + ".friends"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } }
4. 未在Manifest中注册Content Provider
这是最容易忽略的步骤!必须在AndroidManifest.xml的<application>标签内注册你的BirthProvider,否则系统找不到它:
<provider android:name=".BirthProvider" android:authorities="com.mprovider.BirthdayDude" <!-- 必须和PROVIDER_NAME完全一致 --> android:exported="true" <!-- 如果是其他应用访问设为true,同应用内访问可以设为false --> android:grantUriPermissions="true" />
5. SQL语句拼接存在风险和错误
delete和update方法里直接把URI中的ID拼进SQL语句,不仅有SQL注入风险,还可能因为特殊字符导致语法错误。改成用占位符的方式:
修复后的delete方法:
@Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)) { case FRIENDS: count = database.delete(TABLE_NAME, selection, selectionArgs); break; case FRIENDS_ID: String id = uri.getLastPathSegment(); // 用占位符组合条件,避免SQL注入 String combinedSelection = ID + " = ?"; List<String> combinedArgs = new ArrayList<>(); combinedArgs.add(id); if (!TextUtils.isEmpty(selection)) { combinedSelection += " AND (" + selection + ")"; if (selectionArgs != null) { Collections.addAll(combinedArgs, selectionArgs); } } count = database.delete(TABLE_NAME, combinedSelection, combinedArgs.toArray(new String[0])); break; default: throw new IllegalArgumentException("Unsupported URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; }
修复后的update方法:
@Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)) { case FRIENDS: count = database.update(TABLE_NAME, values, selection, selectionArgs); break; case FRIENDS_ID: String id = uri.getLastPathSegment(); String combinedSelection = ID + " = ?"; List<String> combinedArgs = new ArrayList<>(); combinedArgs.add(id); if (!TextUtils.isEmpty(selection)) { combinedSelection += " AND (" + selection + ")"; if (selectionArgs != null) { Collections.addAll(combinedArgs, selectionArgs); } } count = database.update(TABLE_NAME, values, combinedSelection, combinedArgs.toArray(new String[0])); break; default: throw new IllegalArgumentException("Unsupported URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; }
6. Insert方法的参数优化
insert方法里的nullColumnHack参数传了空字符串,不符合API规范,而且没有检查必填字段(NAME和BIRTHDAY是NOT NULL),修复后:
@Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { // 检查参数合法性 if (values == null) { throw new SQLException("ContentValues cannot be null for insertion"); } if (!values.containsKey(NAME) || !values.containsKey(BIRTHDAY)) { throw new SQLException("NAME and BIRTHDAY are required fields (NOT NULL)"); } // 用NAME作为nullColumnHack(当values为空时,插入NAME字段为NULL) long row = database.insert(TABLE_NAME, NAME, values); if (row > 0) { Uri newUri = ContentUris.withAppendedId(CONTENT_URI, row); getContext().getContentResolver().notifyChange(newUri, null); return newUri; } throw new SQLException("Failed to add a new record into " + uri); }
最后检查访问代码
确保你访问Content Provider时用了正确的Uri,比如:
// 查询所有好友记录 Cursor cursor = getContentResolver().query(BirthProvider.CONTENT_URI, null, null, null, BirthProvider.NAME); // 查询ID为1的好友记录 Uri singleUri = ContentUris.withAppendedId(BirthProvider.CONTENT_URI, 1); Cursor singleCursor = getContentResolver().query(singleUri, null, null, null, null);
按照这些步骤修复后,你的BirthProvider应该就能正常工作了。
内容的提问来源于stack exchange,提问作者user9858007
相关产品推荐
相关产品推荐

