美文网首页
ContentProvider

ContentProvider

作者: Drew_MyINTYRE | 来源:发表于2021-07-04 08:15 被阅读0次

它的优点?

ContentProvider 为存储和获取数据提供统一的接口,可以在不同应用程序之间共享数据。

ContentProvider 主要有以下优点:

  • 提供了对底层数据存储方式的抽象。使得无论底层数据存储采用何种方式,外界对数据的访问方式都是统一的,这使得访问简单 & 高效。如底层可以采用 SQLite 方式存储数据,使用 ContentProvider 封装之后,即便底层换成 XML 存储也不会对上层应用代码产生影响。如一开始数据存储方式 采用 SQLite 数据库,后来把数据库换成 MongoDB,也不会对上层数据ContentProvider使用代码产生影响。

  • 为应用间的数据交互提供了一个安全的环境。它准许你把自己的应用数据根据需求开放给其它应用进行操作,而不用担心直接开放数据库权限而带来的安全问题。

进程间 进行数据交互 & 共享,即跨进程通信。ContentProvider 底层是采用 Android 中的 Binder 机制。

ContentProvider, ContentResolver, ContentObserver 之间的关系

  • ContentProvider

内容提供者,主要用于对外提供数据

  • ContentResolver

内容解析者,用于获取内容提供者提供的数据

ContentValues values = new ContentValues();
values.put("id", 1);
values.put("name", name);
       
ContentResolver resolver = getContentResolver();
// 通过ContentResolver 向ContentProvider中插入数据
resolver.insert(uri, values);
  • ContentObserver

注册 ContentObserver 监听 Uri 数据的变化,类似于数据库技术中的触发器(Trigger),当 ContentObserver 所观察的 Uri 发生变化时,便会触发它。触发器分为表触发器、行触发器,相应地 ContentObsever 也分为表ContentObserver、行 ContentObserver,当然这是与它所监听的 Uri MIME Type有关的。

URI

外界进程通过 URI 找到对应 ContentProvider 中的数据,再进行数据操作。

// 表示 匹配 provider 的任何内容
content://com.example.app.provider/*
// 表示 匹配 provider 中的 table 表中的所有行
content://com.example.app.provider/table/#

MIME 数据类型

指定某个文件用某种应用程序来打开

// 根据 URI 返回 MIME 类型
ContentProvider.geType(uri) ;

// MIME类型 = 类型 + 子类型,它是 一个 包含2部分的字符串
// 类型 = text、子类型 = html

text / html

单条记录:

// 若一个Uri如下
content://com.example.transportationprovider/trains/122
// 则ContentProvider会通过ContentProvider.geType(url)返回以下MIME类型
vnd.android.cursor.item/vnd.example.rail

多条记录:

// 若一个Uri如下
content://com.example.transportationprovider/trains
// 则ContentProvider会通过ContentProvider.geType(url)返回以下MIME类型
vnd.android.cursor.dir/vnd.example.rail

Android为常见的数据(如通讯录、日程表等)提供了内置默认的 ContentProvider
但也可根据需求自定义 ContentProvide ,但是必须重写它的 insert / delete / update / query / onCreate / getType 等6个方法。

组织数据方式

ContentProvider主要以表格的形式组织数据,同时也支持文件数据,只是表格形式用得比较多,等同数据库。

注意:

  • ContentProvider 的增删改查操作(外部进程调用),运行在 ContentProvider 进程的Binder 线程池中(不是主线程)
// 由系统进行调用,运行在 ContentProvider 进程的主线程,故不能做耗时操作
public boolean onCreate() {...}
  • 存在多线程并发访问,需要实现线程同步

  • 若 ContentProvider 的数据存储方式是使用 SQLite(单个) ,则不需要,因为 SQLite 内部实现好了线程同步,若是使用多个 SQLite 则需要,因为 SQL 对象之间无法进行线程同步。

  • 若 ContentProvider 的数据存储方式是内存,则需要自己实现线程同步

  • ContentProvider 类并不会直接与外部进程交互,而是通过 ContentResolver 类

进程内通信

// android:exported -> false 只能被自己所在的应用使用
<provider
    android:name=".MyContentProvider"
    android:authorities="com.wwe.myprovider"
    android:enabled="false"
    android:exported="false" />           

进程间进行数据共享

// 进程 A 创建 ContentProvider 存储数据利用 SQLite

<provider
   android:name=".MyProvider"
   android:authorities="com.wwe.PROVIDER"
   android:permission="com.wwe.PROVIDER.ipc"
   android:enabled="true"
   android:exported="true">
</provider>

// 暴露权限
<permission
    android:name="com.wwe.PROVIDER.ipc"
    android:label="provider pomission"
    android:protectionLevel="normal"/>

// 进程/应用 B 要访问进程/应用 A 的数据
<uses-permission android:name="com.wwe.PROVIDER.ipc" />

// 进程 B 获取进程 A 的数据
private void getDataByContentResolver() {
    uri_user = Uri.parse("content://com.wjn.mycontentprovider/user");
    ContentResolver resolver = getContentResolver();
    if (null != resolver) {
        Cursor cursor = resolver.query(uri_user, new String[]{"id", "name", "describe"}, null, null, null);
        StringBuilder sbBuilder = new StringBuilder();
        if (null != cursor) {
            while (cursor.moveToNext()) {
                int id = cursor.getInt(cursor.getColumnIndex("id"));
                sbBuilder.append("ID:" + id + "\n");
                String name = cursor.getString(cursor.getColumnIndex("name"));
                sbBuilder.append("姓名:" + name + "\n");
                String describe = cursor.getString(cursor.getColumnIndex("describe"));
                sbBuilder.append("描述:" + describe + "\n\n\n");
            }
            cursor.close();
        }
    }
}

外部进程通过 ContentResolver类与 ContentProvider 进行交互,ContentResolver 类对所有的 ContentProvider 进行统一管理。

ContentUrisUriMatcherContentObserver 等核心类,自行上网了解一下即可,再次不做讲解。

如果只允许应用 B 获取应用 A 的数据不允许修改的话。该如何做呢?

其实以上 ContentProvider 还可以将访问的权限进一步细化,分成允许读取和允许写入两种。
ContentProvider 设置了读取的权限,那么其他组件想读取到该ContentProvider 的内容时,就必须声明使用读的权限。
ContentProvider 设置了写入的权限,那么其他组件想写入该ContentProvider 的内容时,就必须声明使用写的权限。

ContentProvider 的实现原理?

ContentProvider 核心机制之一也是 Binder,当数据量比较大的时候,继续用Parcel 做容器效率会比较低,因此它还使用了匿名共享内存的方式。

ContentProvider 发布者和调用者这两在 Framework 层是如何实现的。

相关文章

网友评论

      本文标题:ContentProvider

      本文链接:https://www.haomeiwen.com/subject/fhguultx.html