博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 四大组件之 ContentProvider
阅读量:6795 次
发布时间:2019-06-26

本文共 7913 字,大约阅读时间需要 26 分钟。

读前思考

学习一门技术或者看一篇文章最好的方式就是带着问题去学习,这样才能在过程中有茅塞顿开、灯火阑珊的感觉,记忆也会更深刻。

  1. ContentProvider 是什么?
  2. ContentProvider 如何使用?
  3. ContentProvider 和其他通信方式比有什么区别与优点?
  4. 什么是 URI ?怎么书写?有什么含义?

认识 URI

通用资源标志符(Universal Resource Identifier, 简称 "URI")

Uri 代表要操作的数据,Android 上可用的每种资源、图像、视频片段等都可以用 Uri 来表示。Uri 唯一标识每种资源。
Uri一般由三部分组成: 1.访问资源的命名机制。 2.存放资源的主机名。 3.资源自身的名称,由路径表示。
android 的 Uri 由以下三部分组成:
"content://"、数据的路径、标示 ID(可选)

一个标准的内容 URI 写法是这样的:

content://com.example.app.provider/table1//这就表示调用方期望访问的是 com.example.app 这个应用的 table1 表中的数据。复制代码

除此之外,我们还可以在这个内容 URI 的后面加上一个 id,如下所示:

content://com.example.app.provider/table1/1//这就表示调用方期望访问的是 com.example.app 这个应用的 table1 表中 id 为 1 的数据。复制代码

内容 URI 的格式主要就只有以上两种,以路径结尾就表示期望访问该表中所有的数据,以 id 结尾就表示期望访问该表中拥有相应 id 的数据。

我们可以使用通配符的方式来分别匹 配这两种格式的内容 URI,规则如下。

  1. *:表示匹配任意长度的任意字符
  2. #:表示匹配任意长度的数字

所以,一个能够匹配任意表的内容 URI 格式就可以写成:

content://com.example.app.provider/*复制代码

而一个能够匹配 table1 表中任意一行数据的内容 URI 格式就可以写成:

content://com.example.app.provider/table1/#复制代码

什么是 ContentProvider ?

作为四大组件之一,ContentProvider 主要负责存储和共享数据。与文件存储、SharedPreferences 存储、SQLite 数据库存储这几种数据存储方法不同的是,后几者保存下的数据只能被该应用程序使用,而前者可以让不同应用程序之间进行数据共享,它还可以选择只对哪一部分数据进行共享,从而保证程序中的隐私数据不会有泄漏风险。

ContentProvider 有两种形式

  1. 可以使用现有的内容提供者来读取和操作相应程序中的数据。
  2. 也可以创建自己的内容提供者给这个程序的数据提供外部访问接口。

从系统提供的 Provider 访问数据

例子:读取联系人的电话

1.清单文件中添加读取权限

复制代码
  1. 针对 6.0+ 系统动态权限申请
//判断是否有读取联系人权限if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);} else {    readContacts();}复制代码
  1. 重写 onRequestPermissionsResult( ) 方法获取权限申请返回
@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {    super.onRequestPermissionsResult(requestCode, permissions, grantResults);    switch (requestCode) {        case 1:            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                readContacts();            } else {                Toast.makeText(this, "获取权限失败!", Toast.LENGTH_SHORT);            }            break;        default:            break;        }    }复制代码
  1. 获取手机通讯录
private void readContacts() {    List contactsList=null;    Cursor cursor=null;    try {        contactsList=new ArrayList();        //查询联系人数据,使用了getContentResolver().query方法来查询系统的联系人的数据        //CONTENT_URI就是一个封装好的Uri,是已经解析过得常量        cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);        //对cursor进行遍历,取出姓名和电话号码        if (cursor!=null){            while (cursor.moveToNext()){                //获取联系人姓名                String displayName=cursor.getString(cursor.getColumnIndex(                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME                    ));                //获取联系人手机号                String number=cursor.getString(cursor.getColumnIndex(                    ContactsContract.CommonDataKinds.Phone.NUMBER                    ));                //把取出的两类数据进行拼接,中间加换行符,然后添加到listview中                contactsList.add(displayName+"\n"+number);                LogUtils.i("姓名:"+displayName+"\n"+"电话:"+number);                }            }        }catch (Exception e){            e.printStackTrace();        }finally {            //记得关掉cursor            if (cursor!=null){                cursor.close();            }        }    }复制代码
  1. 最后的结果输出
com.keven.jianshu I/TAG: 姓名:孙**    电话:+86157****429com.keven.jianshu I/TAG: 姓名:井**    电话:+861836****299com.keven.jianshu I/TAG: 姓名:张**    电话:+861866****830com.keven.jianshu I/TAG: 姓名:钉**    电话:0105****898com.keven.jianshu I/TAG: 姓名:彩**    电话:0108****604复制代码

创建自己的 Provider

  1. 自定义类继承 ContentProvider,重写六个方法
public class Part1dMyProvider extends ContentProvider {    /**     * 初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,     * 返回 true 表示内容提供器初始化成功,返回 false 则表示失败。注意,只有     * 当存在 ContentResolver 尝试访问我们程序中的数据时,内容提供器才会被初始化。     */    @Override    public boolean onCreate() {        return false;    }    /**     * 从内容提供器中查询数据。使用 uri 参数来确定查询哪张表,projection 参数用     * 于确 定查询哪些列,selection 和 selectionArgs 参数用于约束查询哪些行,     * sortOrder 参数用于 对结果进行排序,查询的结果存放在 Cursor 对象中返回。     */    @Override    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {        return null;    }    /**     * 向内容提供器中添加一条数据。使用 uri 参数来确定要添加到的表,待添加的数据     * 保存在 values 参数中。添加完成后,返回一个用于表示这条新记录的 URI。     */    @Override    public Uri insert(Uri uri, ContentValues values) {        return null;    }    /**     * 更新内容提供器中已有的数据。使用 uri 参数来确定更新哪一张表中的数据,新数     * 据保存在 values 参数中,selection 和 selectionArgs 参数用于约束更新哪些行,     * 受影响的 行数将作为返回值返回。     */    @Override    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {        return 0;    }    /**     * 从内容提供器中删除数据。使用 uri 参数来确定删除哪一张表中的数据,selection     * 和 selectionArgs 参数用于约束删除哪些行,被删除的行数将作为返回值返回。     */    @Override    public int delete(Uri uri, String selection, String[] selectionArgs) {        return 0;    }    /**     * 根据传入的内容 URI 来返回相应的 MIME 类型。 可以看到,几乎每一个方法都会     * 带有 Uri 这个参数,这个参数也正是调用 ContentResolver的增删改查方法时传     * 递过来的。而现在,我们需要对传入的 Uri 参数进行解析,从中分析出 调用方     * 期望访问的表和数据。     */    @Override    public String getType(Uri uri) {        return null;    }}复制代码
  1. 使用 UriMatcher
public class Part1dMyProvider extends ContentProvider {    public static final int TABLE1_DIR = 0;    public static final int TABLE1_ITEM = 1;    public static final int TABLE2_DIR = 2;    public static final int TABLE2_ITEM = 3;    private static UriMatcher uriMatcher; static {        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);        uriMatcher.addURI("com.keven.jianshu.provider", "table1", TABLE1_DIR);        uriMatcher.addURI("com.keven.jianshu.provider ", "table1/#", TABLE1_ITEM);        uriMatcher.addURI("com.keven.jianshu.provider ", "table2", TABLE2_ITEM);        uriMatcher.addURI("com.keven.jianshu.provider ", "table2/#", TABLE2_ITEM);    }    ......}复制代码
  1. 以 query( ) 方法为例示范(insert()、update()、delete() 实现类似)
@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {switch (uriMatcher.match(uri)) {    case TABLE1_DIR:    // 查询table1表中的所有数据        break;    case TABLE1_ITEM:    // 查询table1表中的单条数据        break;    case TABLE2_DIR:    // 查询table2表中的所有数据        break;    case TABLE2_ITEM:    // 查询table2表中的单条数据        break;    default:        break;    }    ……}……}复制代码

除此之外,还有一个方法你会比较陌生,即 getType() 方法。它是所有的内容提供器都必 须提供的一个方法,用于获取 Uri 对象所对应的 MIME 类型。一个内容 URI 所对应的 MIME 字符串主要由三部分组分,Android 对这三个部分做了如下格式规定。

  1. 必须以 vnd 开头。
  2. 如果内容 URI 以路径结尾,则后接 android.cursor.dir/,如果内容 URI 以 id 结尾, 则后接 android.cursor.item/。
  3. 最后接上 vnd..。

所以,对于 content://com.example.app.provider/table1 这个内容 URI,它所对应的 MIME 类型就可以写成:

vnd.android.cursor.dir/vnd.com.example.app.provider.table1复制代码

对于 content://com.example.app.provider/table1/1 这个内容 URI,它所对应的 MIME 类型 就可以写成:

vnd.android.cursor.item/vnd.com.example.app.provider.table1复制代码

则我们的自定义 Provider 可以完善为

@Overridepublic String getType(Uri uri) { switch (uriMatcher.match(uri)) {     case TABLE1_DIR:        return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";    case TABLE1_ITEM:        return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";    case TABLE2_DIR:        return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";    case TABLE2_ITEM:        return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";    default:        break;}return null;}复制代码
  1. 在清单文件中注册
复制代码
  • name:ContentProvider 的全称类名。
  • authorities:唯一标识了一个 ContentProvider,外部应用通过该属性值来访问我们的 ContentProvider。因此该属性值必须是唯一的,建议在命名时以包名为前缀。
  • exported:表明是否允许其他应用调用 ContentProvider,true 表示支持,false 表示不支持。默认值根据开发者的属性设置而会有所不同,如果包含 Intent-Filter 则默认值为 true,否则为 false。

到这里,一个完整的内容提供器就创建完成了,现在任何一个应用程序都可以使用 ContentResolver 来访问我们程序中的数据。

文章已经读到末尾了,不知道最初的几个问题你都会了吗?如果不会的话?可以再针对不会的问题进行精读哦!答案都在文中,相信你肯定可以解决的!

转载于:https://juejin.im/post/5cab6ac35188251afa548943

你可能感兴趣的文章
图形数据库、NOSQL和Neo4j(转载)
查看>>
使用xtrabackup进行Mysql不锁表主从复制
查看>>
Weblogic 共享库部署
查看>>
简述Ubuntu Server修改IP/DNS
查看>>
Word Count Example of Hadoop V1.0 – Mapper的实现
查看>>
CountDownLatch和Cyclicbarrier概念、区别及原理
查看>>
linux网址
查看>>
部署Rap服务记录
查看>>
cent os下编译安装php mongodb扩展教程
查看>>
表单radio点击,取消按钮
查看>>
vuex之module-模块组(六)
查看>>
(二)SpringMVC学习笔记-HelloWorld
查看>>
Centos 6.5下安装升级Python3.3.5
查看>>
MyBatis学习总结(五)——实现关联表查询
查看>>
Spring MVC常用注解说明
查看>>
resin+Apache 整合安装JSP论坛
查看>>
jquery prop()和attr()方法 (总结笔记)
查看>>
Windows蓝屏了怎么办?
查看>>
Ubuntu/Linux常用命令
查看>>
Linux下虚拟内存不足怎么办,如何快速增加swap分区
查看>>