ContentProvider--内容提供者

  内容提供者主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供者是Android实现跨程序共享数据的标准方式。
  不同于文件存储和SharedPreferences存储中的两种全局可读写操作模式,内容提供者可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。

访问其他程序中的数据

  ContentProvider的底层是采用Android中的Binder机制;

  内容提供者的用法一般有两种,一种是使用现有的内容提供者来读取和操作响应程序中的数据,另一种是创建自己的内容提供者给我们程序的数据提供外部访问接口。如果一个应用程序通过内容提供者对其数据提供了外部访问接口,那么任何其他的应用程序就都可以对这部分数据进行访问。

  对于每一个应用程序来说,如果想要访问内容提供者中共享的数据,就一定要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取到该类的实例。ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,其中insert()方法用于添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方法用于查询数据。SQLiteDatabase中也是使用这几个方法来进行CRUD操作的。还可以通过注册ContentObserver来监听数据是否发生了变化来相应地刷新页面。

  不同于SQLiteDatabase,ContentResolver中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容Uri。URI分为系统预置与自定义,分别对应系统内置的数据()和自定义数据库;
  URI是统一资源定位符,URI的格式如下:

[scheme:][//host:port][path][?query]

  我们以一个例子来解释:
URI:http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack

名字 内容
scheme http
host www.baidu.com
port 8080
path wenku/jiatiao.html
query ?之后的东西,id=123456&name=jack

  URI的各个部分在安卓系统中都是可以通过代码来获取的,以上面的例子来解释:

方法 解释
getScheme() 获取Uri中的scheme字符串部分,在上面是http
getHost() 获取Authority中的Host字符串 ,在上面是www.baidu.com
getPort() 获取Authority中的Port字符串,在上面是8080
getPath() 获取Uri中path部分,在上面是wenku/jiatiao.html
getQuery() 获取Uri中的query部分,在上面是id=123456&name=jack

内容Uri给内容提供者中的数据表建立了唯一标识符,它主要由两部分组成:authority和path。authority是用于对不同的应用程序作区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。比如某个程序的包名是com.example.kang,那么该程序对应的authority就可以命名为com.example.kang。path则是用于对同一应用程序中不同的表作区分的,通常都会添加到authority的后面。比如某个程序的数据库中存在两张表:table1和table2,这时就可以将path分别命名为/table1和/table2,然后把authority和path进行组合,内容Uri就变成了com.example.kang/table1和com.example.kang/table2。不过目前还是很难辨认出这两个字符串就是两个内容Uri,还需要在字符串的头部加上协议声明。因此,内容Uri最标准的格式写法是:content://com.example.kang/table1。

  在得到了内容Uri字符串之后,我们还需要把它解析成Uri对象才可以作为参数传入。解析的方法也相当简单,代码如下:

Uri uri = Uri.parse("content://com.example.kang/table1");

  只需要调用Uri.parse()方法,就可以将内容Uri字符串解析成Uri对象了。我们就可以使用这个Uri对象来查询table1表中的数据了。代码如下:

Cursor cursor = getContentResolver().query(
    uri,
    projection,
    selection,
    selectionArgs,
    sortOrder);
query()方法参数 对应SQL部分 描述
uri from table_name 指定查询某个应用程序下的一张表
projection select column1,column2 指定查询的列名
selection where column = value 指定where的约束条件
selectionArgs - 为where中的占位符提供具体的值
orderBy order by column1,column2 指定查询结果的排序方式

  查询完成后返回的是一个Cursor对象,这时我们就可以将数据从Cursor对象中逐个读取出来了。读取的思路仍然是通过移动游标的位置来遍历Cursor的所有行,然后再取出每一行中相应列的数据。代码如下:

if(cursor != null){
    while(cursor.moveToNext()){
        String column1 = cursor.getString(cursor.getColumnIndex("column1"));
        int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
    }
    cursor.close();
}

  向table1表中添加一条数据,就是将待添加的数据组装到ContentValues中,然后调用ContentResolver的insert()方法,将Uri和ContentValues作为参数传入即可。代码如下:

ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);

  想要更新一条新添加的数据,可以借助ContentResolver的update()方法实现,代码如下:

ContentValues values = new ContentValues();
values.put("column1","");
getContentResolver().update(uri,values,"column1 = ? and column2 = ?",new String[]{"text",1});

  可以使用ContentResolver的delete()方法将一条数据删除掉,代码如下:

getContentResolver().delete(uri,"column2 = ?",new String[]{"1"});

创建自己的内容提供者

  ContentProvider是一个抽象类,实际项目中,我们要使用的话,是通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供者。ContentProvider类中有6个抽象方法,我们在使用子类继承它的时候,需要将这六个方法全部重写。代码如下:

public class MyProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}
  • onCreate():
      初始化内容提供者的时候调用。通常会在这里完成对数据库的创建和升级等操作,返回true表示内容提供者初始化成功,返回false则表示失败。注意,只有当存在ContentResolver尝试访问我们程序中的数据时,内容提供者才会被初始化。
  • query():
      从内容提供者中查询数据。使用uri参数来确定查询哪张表,projection参数用于确定查询哪些列,selection和selectionArgs参数用于约束查询哪些行,sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。
  • insert():
      向内容提供者中添加一条数据。使用uri参数来确定要添加到的表,待添加的数据保存在values参数中。添加完成后,返回一个用于表示这条新记录的uri。
  • update():
      更新内容提供者中已有的数据。使用uri参数来确定更新哪一张表中的数据,新数据保存在values参数重,selection和selectionArgs参数用于约束更新哪些行,受影响的行数将作为返回值返回。
  • delete():
      从内容提供者中删除数据。使用uri参数来确定删除哪一张表中的数据,selection和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。
  • getType():
      根据传入的内容uri来返回相应的MIME类型。

  可以看到,几乎每一个方法都会带有uri这个参数,这个参数也正是调用ContentResolver的增删改查方法时传递过来的。我们需要对传入的uri参数进行解析,从中分析出调用方期望访问的表和数据。

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
------------------last line for now-----------------