Android之内容提供者(ContentProvider)

目录

内容提供者(ContentProvider)是Android四大组件之一,用于在不同应用之间共享数据。内容提供者通过统一资源标识符(URI)来标识数据,并使用一套标准的接口来访问和操作数据。它是Android系统中实现跨应用数据共享的主要方式。

作用

  • 数据共享:允许不同应用之间共享数据,例如联系人、媒体文件等。
  • 数据封装:提供统一的接口来访问和操作数据,隐藏底层数据存储的实现细节。
  • 权限控制:通过权限机制控制数据的访问,确保数据的安全性。

基本结构

内容提供者通过继承ContentProvider类来实现,主要包括以下几个方法:

  • onCreate():初始化内容提供者。
  • query():查询数据。
  • insert():插入数据。
  • update():更新数据。
  • delete():删除数据。
  • getType():返回数据的MIME类型。

示例

实现一个简单的内容提供者

1. 创建内容提供者类

cpp 复制代码
public class MyContentProvider extends ContentProvider {

    private static final String AUTHORITY = "com.example.mycontentprovider";
    private static final String PATH = "mydata";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH);

    private SQLiteDatabase database;

    @Override
    public boolean onCreate() {
        // 初始化数据库或其他数据存储
        MyDatabaseHelper dbHelper = new MyDatabaseHelper(getContext());
        database = dbHelper.getWritableDatabase();
        return database != null;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        // 查询数据
        return database.query("mytable", projection, selection, selectionArgs, null, null, sortOrder);
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // 插入数据
        long id = database.insert("mytable", null, values);
        return ContentUris.withAppendedId(CONTENT_URI, id);
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // 更新数据
        return database.update("mytable", values, selection, selectionArgs);
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 删除数据
        return database.delete("mytable", selection, selectionArgs);
    }

    @Override
    public String getType(Uri uri) {
        // 返回数据的MIME类型
        return "vnd.android.cursor.dir/vnd.com.example.mycontentprovider.mydata";
    }
}

2.在AndroidManifest.xml中声明内容提供者

cpp 复制代码
<provider
    android:name=".MyContentProvider"
    android:authorities="com.example.mycontentprovider"
    android:exported="true" />

使用内容提供者

其他应用或组件可以通过ContentResolver来访问内容提供者的数据。

查询数据
cpp 复制代码
Cursor cursor = getContentResolver().query(
    MyContentProvider.CONTENT_URI,
    null, // projection
    null, // selection
    null, // selectionArgs
    null  // sortOrder
);

if (cursor != null) {
    while (cursor.moveToNext()) {
        // 处理查询结果
    }
    cursor.close();
}
插入数据
cpp 复制代码
ContentValues values = new ContentValues();
values.put("column_name", "value");
Uri newUri = getContentResolver().insert(MyContentProvider.CONTENT_URI, values);
更新数据
cpp 复制代码
ContentValues values = new ContentValues();
values.put("column_name", "new_value");
int rowsUpdated = getContentResolver().update(MyContentProvider.CONTENT_URI, values, "column_name = ?", new String[]{"old_value"});
删除数据
cpp 复制代码
int rowsDeleted = getContentResolver().delete(MyContentProvider.CONTENT_URI, "column_name = ?", new String[]{"value"});

内容提供者的URI

内容提供者通过URI来标识和访问数据。URI的结构通常如下:

cpp 复制代码
content://<authority>/<path>/<id>
  • authority:内容提供者的唯一标识符,通常是应用包名。
  • path:数据的路径,表示数据的类型或表名。
  • id:可选,用于标识特定的数据项。

3.权限设置

声明权限

AndroidManifest.xml文件中声明权限,以限制对内容提供者的访问。可以声明读权限、写权限或两者兼有。

cpp 复制代码
<provider
    android:name=".MyContentProvider"
    android:authorities="com.example.mycontentprovider"
    android:exported="true"
    android:readPermission="com.example.mycontentprovider.READ"
    android:writePermission="com.example.mycontentprovider.WRITE" />

在上述示例中,android:readPermission和android:writePermission属性用于指定读和写权限。只有声明了这些权限的应用程序才能访问内容提供者的数据。

定义权限

在AndroidManifest.xml文件中定义自定义权限:

cpp 复制代码
<permission
    android:name="com.example.mycontentprovider.READ"
    android:protectionLevel="normal" />
<permission
    android:name="com.example.mycontentprovider.WRITE"
    android:protectionLevel="normal" />

protectionLevel属性定义了权限的保护级别。常见的保护级别包括:

  • normal:普通权限,系统会自动授予这些权限。
  • dangerous:危险权限,用户需要显式授予这些权限。
  • signature:签名权限,只有与内容提供者应用具有相同签名的应用才能获得这些权限。

在代码中检查权限

在内容提供者的代码中,可以使用checkCallingPermission()方法来检查调用应用是否具有所需的权限。

cpp 复制代码
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    if (getContext().checkCallingPermission("com.example.mycontentprovider.READ") != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Permission denied to read data");
    }
    // 执行查询操作
    return database.query("my_table", projection, selection, selectionArgs, null, null, sortOrder);
}

@Override
public Uri insert(Uri uri, ContentValues values) {
    if (getContext().checkCallingPermission("com.example.mycontentprovider.WRITE") != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Permission denied to write data");
    }
    // 执行插入操作
    long id = database.insert("my_table", null, values);
    return ContentUris.withAppendedId(CONTENT_URI, id);
}

动态权限请求

对于危险权限,调用应用需要在运行时请求权限。

cpp 复制代码
// 请求读权限
if (ContextCompat.checkSelfPermission(this, "com.example.mycontentprovider.READ") != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{"com.example.mycontentprovider.READ"}, REQUEST_CODE);
}

// 请求写权限
if (ContextCompat.checkSelfPermission(this, "com.example.mycontentprovider.WRITE") != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{"com.example.mycontentprovider.WRITE"}, REQUEST_CODE);
}

处理权限请求结果

在调用应用中,处理权限请求结果:

cpp 复制代码
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_CODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限授予,执行相应操作
        } else {
            // 权限被拒绝,显示提示信息
        }
    }
}

应用场景

1. 应用之间的数据共享

内容提供者允许不同应用之间共享数据。例如,一个联系人应用可以通过内容提供者共享联系人信息,其他应用可以访问这些联系人数据。

联系人应用

Android系统自带的联系人应用使用内容提供者来管理联系人数据,其他应用可以访问这些数据以获取联系人信息。

媒体应用

音乐播放器应用可以通过内容提供者共享音乐文件信息,其他应用可以访问这些音乐文件以播放或管理音乐。

2. 应用内部的数据管理

内容提供者不仅可以在应用之间共享数据,还可以在应用内部不同组件之间共享数据。例如,一个应用的多个Activity或Fragment可以通过内容提供者共享和管理数据。

新闻应用

一个新闻应用的不同Activity或Fragment可以通过内容提供者共享新闻数据。

任务管理应用

一个任务管理应用的不同组件可以通过内容提供者共享和管理任务数据。

3. 数据同步

内容提供者可以与同步适配器(SyncAdapter)结合使用,实现数据的同步。例如,一个邮件应用可以使用内容提供者和同步适配器同步邮件数据。

邮件应用

邮件应用可以使用内容提供者存储邮件数据,并通过同步适配器实现邮件数据的同步。

笔记应用

笔记应用可以使用内容提供者存储笔记数据,并通过同步适配器实现笔记数据的同步。

4. 数据的统一访问接口

内容提供者提供了统一的接口来访问和操作数据。通过内容提供者,应用程序可以使用标准的URI和CRUD操作接口来访问和管理数据,而不需要关心底层数据存储的实现细节。

文件管理应用

文件管理应用可以使用内容提供者提供的统一接口访问和管理不同类型的文件数据。

设置应用

系统设置应用可以使用内容提供者提供的统一接口访问和管理系统设置数据。

5. 数据的安全访问

内容提供者可以通过权限控制来实现数据的安全访问。应用程序可以通过内容提供者设置权限,限制其他应用对数据的访问。

健康数据应用

健康数据应用可以使用内容提供者存储健康数据,并通过权限控制限制其他应用对健康数据的访问。

财务管理应用

财务管理应用可以使用内容提供者存储财务数据,并通过权限控制限制其他应用对财务数据的访问。

6. 数据的备份和恢复

内容提供者可以与备份服务结合使用,实现数据的备份和恢复。例如,一个日历应用可以使用内容提供者和备份服务备份日历数据。

日历应用

日历应用可以使用内容提供者存储日历数据,并通过备份服务实现日历数据的备份和恢复。

联系人应用

联系人应用可以使用内容提供者存储联系人数据,并通过备份服务实现联系人数据的备份和恢复。

7. 数据的观察与监听

内容提供者可以与内容观察者(ContentObserver)结合使用,实现对数据变化的观察和监听。例如,一个短信应用可以使用内容观察者监听短信数据的变化。

短信应用

短信应用可以使用内容观察者监听短信数据的变化,并在有新短信时通知用户。

股票应用

股票应用可以使用内容观察者监听股票数据的变化,并在股票价格变化时通知用户。

应用场景总结

内容提供者在Android应用开发中有广泛的应用场景,包括应用之间的数据共享、应用内部的数据管理、数据同步、数据的统一访问接口、数据的安全访问、数据的备份和恢复以及数据的观察与监听。通过内容提供者,开发者可以实现数据的高效管理和安全共享,提高应用的功能性和用户体验。

Activity之Intent、生命周期、启动模式
Android之service两种启动方式的异同
Android之Service与IntentService区别
Android之广播(Broadcast)
ANR 超时的定义

相关推荐
CYRUS_STUDIO38 分钟前
Android 源码如何导入 Android Studio?踩坑与解决方案详解
android·android studio·源码阅读
前端赵哈哈2 小时前
初学者入门:Android 实现 Tab 点击切换(TabLayout + ViewPager2)
android·java·android studio
一条上岸小咸鱼5 小时前
Kotlin 控制流(二):返回和跳转
android·kotlin
Jasonakeke5 小时前
【重学 MySQL】九十二、 MySQL8 密码强度评估与配置指南
android·数据库·mysql
Mertrix_ITCH5 小时前
在 Android Studio 中修改 APK 启动图标(2025826)
android·ide·android studio
荏苒追寻5 小时前
Android OpenGL基础1——常用概念及方法解释
android
人生游戏牛马NPC1号5 小时前
学习 Android (十七) 学习 OpenCV (二)
android·opencv·学习
恋猫de小郭6 小时前
谷歌开启 Android 开发者身份验证,明年可能开始禁止“未经验证”应用的侧载,要求所有开发者向谷歌表明身份
android·前端·flutter
用户096 小时前
Gradle声明式构建总结
android
用户097 小时前
Gradle插件开发实践总结
android