You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

访问自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:08:19