Android组件---ContentProvider

173 阅读6分钟

Android组件---ContentProvider

1.定义与作用

// 定义:
    四大组件之一,内容提供者,使用内容提供器是目前Android中实现跨进程共享数据的标准方式。
    
//作用:
    进程间进行数据交互&共享,即跨进程通信。
    
    注意:
        1)ContentProvider = 中间者角色,真正存储和操作的数据源还是原来存储数据的方式(数据库、文件、xml或网络)
        2)数据源:数据库、文件、网络、XML等等
​

2.用法简介

内容提供器的用法一般有两种:
    1.使用现有的内容提供器来读取和操作相应程序中的数据
    2.创建自己的内容提供器给我们应用程序的数据提供外部访问接口。

3.使用ContentResolver而非直接访问ContentProvider?

// ContentResolver的作用
    即:统一管理不同ContentProvider间的操作
        1)通过URI即可操作不同的ContentProvider中的数据
        2)外部进程通过ContentResolver类从而和ContentProvider类进行交互
        
// 为什么要使用ContentResolver类与ContentProvider交互,而非直接访问ContentProvider?        
    原因:一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而再完成数据交互,操作成本高且难度大。
         在ContentProvider类上多加一个ContentResolver类,便于对所有的ContentProvider进行统一管理。
        

4.访问其它程序中的数据

// 第一步:借助ContentResolve类,通过Context.getContentResolver方法获取
    ContentResolver contentResolver = getContentResolver();
        或者
    ContentResolver contentResolver = context.getContentResolver();     
​
// 第二步:获取内容URI(ContentResolver中的增删改查不接收表名参数,而是使用Uri参数代替,这个参数称为内容URI)
    内容URI标准格式写法:
        content://com.excample.app.provider/table1
        content://com.excample.app.provider/table2
​
    通配符*与#
        1)*:表示匹配任意长度的任意字符
        2)#:表示匹配任意长度的数字
        所以,一个能匹配任意表的内容URI格式就可以写成:
            content://com.excample.app.provider/*
​
        而一个能够匹配table表中任意一行数据的内容URI格式就可以写成:          
            content://com.excample.app.provider/table1/#
​
    格式解析:
        内容URI给内容提供器中的数据建立了唯一标识符,由权限和路径组成;
        1)内容协议:协议声明,如上述中的content://
        2)权限:用于对不同的程序作区分的,为了避免冲突,都会使用程序包名的方式命名,如上述的:com.excample.app.provider
        3)路径:用于对同一应用程序中不同的表做区分的,通常会添加在权限的后面,例如table1和table2.
​
// 第三步:解析内容URI字符串为Uri对象
    解析:(解析的关键点在于Uri.parse方法)
        Uri uri = Uri.parse("content://com.excample.app.provider/table1");            
​
// 第四步:增删改查的操作
    1)查询table1表中的内容
        // 查询完成后返回的是一个cursor对象,
        Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
        // 将数据从Cursor对象中逐个读取出来
        if(cursor != null){
            // 读取方式:通过移动游标的位置来遍历Cursor的所有行,然后再取出每一行中相应列的数据 
            while(cursor.moveToNext()){
                String column1 = cursor.getString(cursor.getColumnIndex("column1"));
                int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
            }
            cursor.close();
        }
            
    2)向table1中增加数据
        // 将待添加的数据组装到ContentValues中
        ContentValues values = new ContentValues();
        values.put("column1","text");
        values.put("column2",1);
        // 调用ContentResolver的insert方法,将Uri和ContentValues作为参数传入即可。
        getContentResolver().insert(uri,values);
​
    3)更新table1中的数据
        ContentValues values = new ContentValues();
        values.put("column1","");
        // 使用selection和selectionArgs参数来对想要更新的数据进行约束,以防所有的行都会受到影响
        getContentResolver().update(uri,values,"column1=? and column2=?",new String[]{"text","1"});
        
    4)删除table1中的数据
        // 调用delete方法将这条数据删除掉
        getContentResolver().delete(uri,"column2 = ?",new String[]{"1"});

5.内容URI对应的MIME

一个内容URI对应的MIME字符串主要由三个部分组成,Android对这三个部分做了如下规定:
1.必须以vnd开头
2.如果内容URI以路径结尾,则后接android.cursor.dir/,
  如果内容URI以id结尾,则后接android.cursor.item/
3.最后接上vnd.<authority>.path
​
所以,对于content://com.excample.app.provider/table1这个内容URI,它所对应的MIME类型就可以写成:
        vnd.android.cursor.dir/vnd.com.excample.app.provider.table1
            
     对于content://com.excample.app.provider/table1/1这个内容URI,它所对应的MIME类型就可以写为:
        vnd.android.cursor.item/vnd.com.excample.app.provider.table1
        

6.进程内通信

// 步骤:
    1) 创建数据库类,增加相对应的增删改查逻辑
    2)自定义ContentProvider类,重写对应方法
    3)在AndroidManifest文件中注册自定义的ContentProvider
    4)进程内访问ContentProvider
        
// 实例参考:(5.1部分)             
    https://blog.csdn.net/xyl826/article/details/100974149#t22

7.进程间通信

// 思路:
    进程1:创建ContentProvider,存储数据(采用SQLite)
    进程2:访问ContentProvider中存储的数据
        
// 具体步骤
    进程1:
        1)创建数据库类
        2)自定义ContentProvider
        3) 在AndroidManifest文件中注册创建的ContentProvider类
    进程2:
        1)在AndroidManifest文件中声明可访问的权限(声明的权限必须与进程1中设置的权限对应)
        2)访问ContentProvider的类
        
// 实例参考:(5.2部分)             
    https://blog.csdn.net/xyl826/article/details/100974149#t22        

8.辅助ContentProvider的工具类

// ContentUris类
作用:操作URI
核心方法:withAppendedId、parseId    
    
示例1:ContentUris.withAppendedId
    Uri uri = Uri.parse("content://com.excample.app.provider/user");
    Uri resultUri = ContentUris.withAppendedId(uri,7);
    生成的Uri为:content://com.excample.app.provider/user/7
​
示例2:ContentUris.parseId
    Uri uri = Uri.parse("content://com.excample.app.provider/user/7");
    long personId = ContentUris.parseId(uri);
    获取的结果为:7// UriMatcher类
作用:
    1)在ContentProvider中注册URI
    2)根据URI匹配ConentProvider中对应的数据表        
​
具体使用:        
    1)初始化UriMatcher对象
        UriMatcher matcher = new UriMathcer(UriMathcer.NO_MATCH);
        注:常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码,即初始化的时候不匹配任何东西
        
    2)在ContentProvider中注册URI(add URI)
        int URI_CODE_a = 1;
        int URI_CODE_b = 2;
        
        // 若URI资源路径 = content://com.excample.provider/user1,则返回注册码URI_CODE_a
        matcher.addURI("com.excample.provider","user1",URI_CODE_a);
​
        // 若URI资源路径 = content://com.excample.provider/user2,则返回注册码URI_CODE_b
        matcher.addURI("com.excample.provider","user2",URI_CODE_b);
    
    3)根据URI匹配URI_CODE,从而匹配ContentProvider中相应的资源(match方法)        
        @Override
        public String getType(Uri uri){
            Uri uri = Uri.parse("content://com.excample.provider/user1");
            switch(matcher.match(uri)){
                case URI_CODE_a:
                    return tableNameUser1;
                case URI_CODE_b:
                    return tableNameUser2;
            }
        }
​
// ContentObserver类
定义:内容观察者
作用:观察Uri引起ContentProvider中的数据变化并且通知外界(即访问该数据的访问者)    
核心概念:当ContentProvider中的数据发生变化(增、删、改时,就会触发该ContentObserver类)    
    
具体使用:
    1)注册内容观察者ContentObserver
        // 通过ContentResolver类进行注册,并指定需要观察的URI
        getContentResolver().registerContentObserver(uri);  
    
    2)当ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
        public class UserContentProvider extend ContentProvider{
            public Uri insert(Uri uri,ContentValues values){
                db.insert("user","userId",values);
                // 通知访问者
                getContext().getContentResolver().notifyChange(uri,null);
            }
        }
        
    3)解除观察者
        // 通过ContentResolver类进行解除
        getContentResolver().unregisterContentObserver(uri);
    

9.优点

// 1.安全
    ContentProvider为应用间的数据交互提供了一个安全的环境        
        
// 2.访问简单且高效
        
    相对于其他对外共享数据的方式,数据访问方式会因数据存储的方式而不同,这使得访问数据变得复杂且难度大;
        1)采用文件方式对外共享数据,需要进行文件操作读写数据
        2)采用SharedPreferences共享数据,需要使用SharedPr        
        eference API读写数据
        
    而ContentProvider方式,其解耦了底层数据的存储方式,使得无论底层数据存储何种方式,外界对数据的访问方式都是统一的,
    这使得访问简单且高效。
        
    例如:项目刚开始采用SQLite数据库,后来把数据库换为GreenDao,也不会对上层数据ContentProvider使用代码产生影响。