美文网首页
Android四大组件之ContentProvider

Android四大组件之ContentProvider

作者: 彭空空 | 来源:发表于2019-10-04 16:22 被阅读0次

导读

Android四大组件之ContentProvider简介

Content provider管理android以结构化方式存放的数据。他以相对安全的方式封装数据并且提供简易的处理机制。Content provider提供不同进程(应用程序)间数据交互的标准化接口,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。
目前,使用内容提供器是Android实现跨进程共享数据的标准方式。
不同于文件存储和SharedPreferences存储中的两种全局可读写操作模式,内容提供者可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄露的风险。

定义

  • 管理对结构化数据集的访问,封装数据并提供用于定义数据安全性的机制.
  • ContentProvider是Android系统中提供的专门用户不同应用间进行数据共享的组件,提供了一套标准的接口用来获取以及操作数据,准许开发者把自己的应用数据根据需求开放给其他应用进行增删改查,而无须担心直接开放数据库权限而带来的安全问题。

作用

  • 进程间进行数据交互 & 共享,就是跨进程通信.也可以进行进程内通信.
  • 跨进程通信示意图

原理

  • 底层采用的是Android中的Binder机制

具体使用

统一资源标识符(URI)

标识数据的URI, URI包括整个提供程序的符号名称(授权)和一个指向表的名称(路径)。

外界进程可以通过URI找到对应的ContentProvider&其表内的数据,然后再进行操作。

URI 分为 系统预置 和 自定义两种 , 分别对应系统内置的数据(通讯录,日历等) 和自定义数据库

  Uri uri = Uri.parse("content://com.carson.provider/User/1")
//标明 URI指向的资源是名为 "com.carson.provider"中ContentProvider中的表名为User,ID为1的数据
//特别注意: URI模式存在匹配通配符 * 和 #
// *:匹配任意长度的任何有效字符的字符串
// 以下的URI 表示 匹配provider的任何内容
  content://com.example.app.provider/* 
// #:匹配任意长度的数字字符的字符串
// 以下的URI 表示 匹配provider中的table表的所有行
  content://com.example.app.provider/table/# 

MIME 数据类型

指定某个扩展名的文件用某种应用程序来打开.比如.txt的文件用 text文件打开ContentProvider 根据 URI 返回MIME的类型。

ContentProvider.getType(uri);

每种MIME类型由2个部分组成= 类型+子类型, 就是说MIME类型是包含了2个部分的字符串:

text/html
application/pdf

ContentProvider类

ContentProvider 主要以 表格的形式 组织数据,同时也支持文件数据,ContentProvider的核心方法增删改查数据(进程间共享数据的本质)

//<-- 4个核心方法 -->
// 外部进程向 ContentProvider 中添加数据
 public Uri insert(Uri uri, ContentValues values) 

// 外部进程 删除 ContentProvider 中的数据
 public int delete(Uri uri, String selection, String[] selectionArgs) 

// 外部进程更新 ContentProvider 中的数据
 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

// 外部应用 获取 ContentProvider 中的数据
 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder)  

// 注:
// 1. 上述4个方法由外部进程回调,并运行在ContentProvider进程的Binder线程池中(不是主线程)
// 2. 存在多线程并发访问,需要实现线程同步
    // a. 若ContentProvider的数据存储方式是使用SQLite & 一个,则不需要,因为SQLite内部实现好了线程同步,若是多个SQLite则需要,因为SQL对象之间无法进行线程同步
    // b. 若ContentProvider的数据存储方式是内存,则需要自己实现线程同步

//<-- 2个其他方法 -->
// ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider,运行在ContentProvider进程的主线程,故不能做耗时操作时 由系统进行调用
public boolean onCreate() 

// 得到数据类型,即返回当前 Url 所代表数据的MIME类型
public String getType(Uri uri)

ContentResolver类

ContentResolver统一管理不同ContentProvider间的操作

  1. 通过URI即可操作不同ContentProvider中的数据
  2. 外部进程通过ContentResolver类与ContentProvider进行交互.

问:为什么不直接只用ContentProvider进行交互,中间还加了一层ContenResolver?

  1. 如果app要和多个ContentProvider进行交互,实现不同的ContentProvider再完成数据交互,操作成本高难度也大, 所以在ContentProvider类上再加上一层ContentResolver类对所有的ContentProvider类进行统一管理.

核心方法:

// 外部进程向 ContentProvider 中添加数据
public Uri insert(Uri uri, ContentValues values)  

// 外部进程 删除 ContentProvider 中的数据
public int delete(Uri uri, String selection, String[] selectionArgs)

// 外部进程更新 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)  

// 外部应用 获取 ContentProvider 中的数据
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

使用时:

// 使用ContentResolver前,需要先获取ContentResolver
// 可通过在所有继承Context的类中 通过调用getContentResolver()来获得ContentResolver
ContentResolver resolver =  getContentResolver(); 

// 设置ContentProvider的URI
Uri uri = Uri.parse("content://cn.scu.myprovider/user"); 

// 根据URI 操作 ContentProvider中的数据
// 此处是获取ContentProvider中 user表的所有记录 
Cursor cursor = resolver.query(uri, null, null, null,"userid desc"); 

ContentUris类

ContentUris的作用就是操作URI,核心方法:withAppendedId()、parseId()。

// withAppendedId()作用:向URI追加一个id
Uri uri = Uri.parse("content://cn.scu.myprovider/user") 
Uri resultUri = ContentUris.withAppendedId(uri, 7);  
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7

// parseId()作用:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7") 
long personid = ContentUris.parseId(uri); 
//获取的结果为:7 

UriMatcher类

UriMatcher的作用是:

  • 在ContentProvider注册 URI
  • 根据URI匹配ContentProvider中对应的数据表
// 步骤1:初始化UriMatcher对象
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 
//常量UriMatcher.NO_MATCH  = 不匹配任何路径的返回码
// 即初始化时不匹配任何东西

// 步骤2:在ContentProvider 中注册URI(addURI())
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a); 
matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b); 
// 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b

// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())

@Override   
public String getType(Uri uri) {   
  Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");   

  switch(matcher.match(uri)){   
 // 根据URI匹配的返回码是URI_CODE_a
 // 即matcher.match(uri) == URI_CODE_a
  case URI_CODE_a:   
    return tableNameUser1;   
    // 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
  case URI_CODE_b:   
    return tableNameUser2;
    // 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
}   
}

ContentObserver类

内容观察者,观察ContentProvider中的数据变化(增删改) 、通知外界。

// 步骤1:注册内容观察者ContentObserver
getContentResolver().registerContentObserver(uri);
// 通过ContentResolver类进行注册,并指定需要观察的URI

// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider { 
  public Uri insert(Uri uri, ContentValues values) { 
  db.insert("user", "userid", values); 
  getContext().getContentResolver().notifyChange(uri, null); 
  // 通知访问者
 } 
}

// 步骤3:解除观察者
getContentResolver().unregisterContentObserver(uri);
// 同样需要通过ContentResolver类进行解除

创建自己的内容提供器

public class MyContentProvider extends ContentProvider {
    public MyContentProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

相关文章

网友评论

      本文标题:Android四大组件之ContentProvider

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