Android内容提供者ContentProvider总结

来源:互联网 发布:3d肌肉软件 编辑:程序博客网 时间:2024/05/29 17:17

一.ContentProvider基础知识

       ContentProvider作为Android四大组件之一,相对其他组件来说,应用的场合是最少的了,但是它是有自己独特的作用的,比如为其他程序提供数据。ContentProvider和其他四个组件一样,使用自己编写的类时,要在AndroidManifest里面注册。
       ContentProvider内部如何保存数据由其设计者决定 .但所有的ContentProvider都实现了一组通的方法来提供数据的增删改查功能.
       客户端通常不会直接使用这些方法,大多数据是通过ContentResolver对象实现对ContentProvider的操作.开发人员可以通过调用Activity或者其它应用程序组伯的实现类中的 getContentResolver()方法来获得Content Provider对象,
如: ContentResolver cr=getContentResolver();
       使用ContentResolver提供的方法可以获得Content Provider中任何感兴趣的数据.
不同进程之间通信由 ContentProvider类和ContentResolver类处理.

(一)数据模型

ContentProvider使用基本数据库模型的简单表格来提供其中的数据,这里每行代表一条记录, 每列代表特定类型和含义的数据.例如,联系人的信息可以如下

_IDNAMENUMBEREMAIL001张三123******123******@163.com002李四132******132******@google.com003王五414******414******@qq.com

每条记录包含一个数值型的_ID字段,它用于在表格中唯一标记该记录.ID
每条记录包含一个数值型的_ID字段,它用于在表格中唯一标记该记录.ID能用于匹配相关表格中 的记录,例如在一个表格中查询联系人电话,在另一个表格中查询照片 查询返回一个Cursor对象,它能遍历各行各列来读取各个字段的值.对于各个类型的数据,Cursor 对象都提供专用的方法. 因此,为了读取字段的数据,开发人员必须知道当前字段包含的数据类型。
其实这个增删改查的操作实现和android数据库的操作是底层实现是一样的,只是我们客户端操作调用的是写好的方法来实现,基本的数据的关键字还是要我们自己来填写的。

(二)URI的用法

       对于ContentProvider来说Uri是非常需要理解的知识。
       在电脑术语中,统一资源标识符(Uniform Resource Identifier,或URI)是一个用于标识某一 互联网资源名称的字符串。 该种标识允许用户对任何(包括本地和互联网)的资源通过特定 的协议进行交互操作。URI由包括确定语法和相关协议的方案所定义。
每个ContentProvider提供公共的URI来唯一标识其数据集,管理多个数据集(多个表格)的 ContentProvider为每个都提供了单独的URI.
       也就是说每一个Uri对于一个表格,我们可以通过某个Uri来实现对某个表格进行相应的操作,比如找到电话号码表格的Uri才能读取里面的通话数据。
所提供的URI都以content://作为前 缀”content://”模式表示数据由Content Provider来管理 如果自定义Content Provider,则应该也是其URI定义一个常量,以简化客户端代码并让日后更 新更加简洁.
       Android为当前平台提供的Content Provider定义了CONTENT_URI常量.匹配电 话号码到联系人表格的URI和匹配保存联系人照片表格的URI分别如下. android.provider.Contacts.Phones.CONTENT.URI; android.provider.Contacts.Photos.CONTENT.URI;
URI常量用于所有与Content Provider的交互中.每个ContentResolver方法使用URI作为其第 一个参数.它标识ContentResolver应该使用哪个provider以及其中的哪个表格.
下面是Content URI重要部分的总结
比如Uri:content://com.hua.employeeprovider/dba/001
上面的URI分四部分
第一部分:标准的前缀(content://),用于标识该数据由Content Provider管理,它永远不用修改。
第二部分:URI的authority部分,它标识该Content Provider.对于第三方应用,该部分一般是完整的包名(小写)来保证唯一性。
如果是自定义的ContentProvider要在AndroidManifest中的provider声明authority.
第三部分: Content Provider的路径部分,用于决定哪类数据被请求,如果ContentProvider仅提供一种数据类型,这部分可以没有.如果提供几种类型,包括子类型,这部分可以由几部分组成.比如对数据库不同表格或对表格的不同操作都可以使用路径来区分。
第四部分:被请求的特定的记录的ID值.这是被请求记录的_ID值.如果请求不仅限于单条记录该部分及前面的斜线应该删除。

二.系统中预定义的ContentProvider

       Android系统为常用数据类型提供了很多预定义的ContentProvider(声音,视频,图片,联系人), 它他大部分位于android.provider包中,开发人员可以查询这些provider经获得其中包含的信息.android系统提供的常见Content Provider说明如下
1.Browser:读取或修改书签,浏览历史或网络搜索.
2.CallLog:查看或更新通话历史.
3.Contacts:获取、修改或保存联系人
4.LiveFolders:由Content Provider提供 内容的特定文件夹
5.MediaStrore:访问声音,视频和图片
6.Setting:查看和获取蓝牙设置,铃声和其它设备偏好. 7.earchRecentSuggestions:该类能为应用程序创建简单的查询建议提供者,它基于近期查询提供建议.
8.SyncStateContract:用于使用数据数组帐号关联数据的Content Provider约束, 希望使用标准方式保存数据的provider可以使用它.
9.UserDictionary:在可以预测文本输入时,提供用户定义的单词给输入法使用.应用程 序和输入法能增加到该字典,单词能关联频率信息和本地化信息.
其中上面大部分的ContentProvider并不常用,常看联系人信息是比较常用的。

三.查询数据

在ContentProvider中查询数据,开发人员需要知道以下信息:

(一)标识该Content Provider的URI

(二)需要查询的数据字段名称和字段中数据的类型 如果需要查询特定记录,那么还需要知道该记录的ID值.

一般使用ContentResolver对象的query()方法完成查询功能,都返回Cursor对象.
query()方法的声明如下
Public final Cursor query(Uri uri,String[] projection,String selection, String[] selectionArgs,String sortOrder)
参数一:Uri:用于查询的Content Providerr URI值
参数二:Projection:由需要查询的列名组成的数组,如果为NULL则不胜枚举查询全部列
参数三:Selection:类似SQL中的WHERE子句,用于增加条件来完成数据过虑
参数四:SelectionArgs:用于替换selection中可以使用?表示的变量值
参数五:sortOrder:类似SQL中的ORDER BY 子句,用于实现排序功能
返回值:Cursor对象,它位于第一条记录之前,或者为NULL
为了限制仅返回一条记录,可以在URL结尾加该记录的_ID值,即将匹配ID值的字符串作为URI路径部分的结尾片段.

四.自定义ContentProvider

如果开发人员希望共享自己的数据,可以自定义Content Provider 步骤如下
建立数据存储系统,大多数ContentProvider使用Android文件存储方法或者SQLite数据库保存数据,但是开发人员可以使用任何方式存储.

(一)使用自定义的ContentProvider必须要做的:

1.继承ContentProvider类来提供数据访问方式。

实现ContentProvider里面的六个方法,
(1)重写onCreate方法里面创建数据库
(2)重写增删改查操作的方法,也可以不用全部重写,比如你只想让其他程序访问本程序的数据,而不能进行其他的操作,只需要重写query方法就可以了,其他方法可以不重写。

2.在应用程序的AndroidManifest文件中声明ContentProvider。

代码如下:

(二)UriMatcher

UriMatcher类主要用于规范匹配Uri。
比如给Uri添加路径和ID号。
系统判断到相应的类别就做相应的操作。
使用方法如下。

1.初始化:

UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

2.注册需要的Uri:

matcher.addURI(“com.whunf”, “people”, PEOPLE);
或matcher.addURI(“com.whunf”, “person/#”, PEOPLE_ID);
一个表的不同操作可以注册多个Uri。

3.与已经注册的Uri进行匹配:

(1) Uri uri = Uri.parse(“content://” + “com.whunf” + “/people”); 这个uri是访问者填写的。
(2)int match = matcher.match(uri);
这里的match是在ContentProvider里面定义的,做判断和匹配也是在ContentProvider里面进行的。
switch (match) {
case PEOPLE:
return “vnd.android.cursor.dir/people”;
case PEOPLE_ID:
return “vnd.android.cursor.item/people”;
default:
return null;  
}
match方法匹配后会返回一个匹配码Code,即在使用注册方法addURI时传入的第三个参数。 上述方法会返回”vnd.android.cursor.dir/person”.
总结:
常量 UriMatcher.NO_MATCH 表示不匹配任何路径的返回码
# 号为通配符 * 号为任意字符

官方SDK说明中关于Uri的注册是这样写的:
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(“contacts”, “/people”, PEOPLE); sURIMatcher.addURI(“contacts”, “/people/#”, PEOPLE_ID);
sURIMatcher.addURI(“contacts”, “/people/#/phones”, PEOPLE_PHONE S);
sURIMatcher.addURI(“contacts”, “/people/#/phones/#”, PEOPLE_PHO NES_ID);
sURIMatcher.addURI(“contacts”,”/people/#/contact_methods”,PEO PLE_CONTACTMETHODS);
sURIMatcher.addURI(“contacts”, “/people/#/contact_methods/#”, P EOPLE_CONTACTMETHODS_ID);
sURIMatcher.addURI(“contacts”, “/deleted_people”, DELETED_PEOPL E); sURIMatcher.addURI(“contacts”, “/phones”, PHONES); sURIMatcher.addURI(“contacts”, “/phones/filter/“, PHONES_FILTE R); sURIMatcher.addURI(“contacts”, “/phones/#”, PHONES_ID); sURIMatcher.addURI(“contacts”, “/contact_methods”, CONTACTMETHO DS); sURIMatcher.addURI(“contacts”, “/contact_methods/#”, CONTACTMET HODS_ID); sURIMatcher.addURI(“call_log”, “/calls”, CALLS); sURIMatcher.addURI(“call_log”, “/calls/filter/“, CALLS_FILTER) ; sURIMatcher.addURI(“call_log”, “/calls/#”, CALLS_ID);
}
其实上面只是提供一种书写的规范,提供我们参考。也可以按照自己的思路来注册Uri。

(三)ContentUris

ContentUris 类用于获取Uri路径后面的ID部分
为路径加上ID的方法: withAppendedId(uri, id)
比如有这样一个Uri
Uri uri = Uri.parse(“content://com.whunf/people”)
通过withAppendedId方法,为该Uri加上ID
1. Uri resultUri = ContentUris.withAppendedId(uri, 10);
最后resultUri的字符串为: content://com.whunf/people/10
从路径中获取ID: parseId(uri)
1. Uri uri = Uri.parse(“content://com.whunf/people/10”)
2. long personid = ContentUris.parseId(uri);
最后personid 为 :10

五.下面是一个访问手机联系人的信息的使用示例

程序分析:
手机联系人的信息是在Android中某个固定的位置的,
它的Uri也是系统规定好的,需要我们去查。
手机联系人里面不止一个表,字段也很多,这个也是需要我们了解后,才能取得它对应的信息。
里面很多信息我们可以把手机电话信息的数据导出后再,研究里面的元素,这里打开手机数据库文件的工具一般使用的是:SQLite Expert Professional视图根据。
下面是查询所有用户的姓名和号码的Java代码

package com.lwz.provider;import android.content.ContentResolver;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.provider.ContactsContract;import android.support.v7.app.AppCompatActivity;import android.util.Log;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //操作电话本数据        selectPhone();    }    private void selectPhone() {        //获得ContentResolver对象        ContentResolver resolver = getContentResolver();        //获得系统定义的电话某一个表格的Uri(这里对应的是数据库的contacts表,这个表主要数存放用户的ID)        Uri uri = ContactsContract.Contacts.CONTENT_URI;//这是系统类定义好的字符串的Uri        //等同 Uri uri =Uri.parse("content://com.android.contacts/contacts" );        //获取该Uri下的信息        Cursor cursor = resolver.query(uri, null, null, null, null);        //创建一个List集合存放用户对象的数据        List<User> list = new ArrayList<>();        //遍历取出数据,这里涉及到多表查询,因为数据存储是分类在多个表中        while (cursor.moveToNext()) {            //获得用户的Id,参数数字段名称的字符串            long id = cursor.getLong(cursor.getColumnIndex("name_raw_contact_id"));            //Log.e("Tag", "id=" + id);            //创建一个User来存储信息            User user = new User();            user.id = id;            //根据获取的到的用户的ID,来分别获取它的数据,这些数据又是在另一个表中            //获得系统定义的电话某一个表格的Uri(这里对应的是数据库的data表,这个表里面有最全面的信息)            //正是因为由于data表里面的数据最多,所以显示或排列的顺序不一定正常,需要我们提供一个ID来查找对应的数据            Uri uri2 = ContactsContract.Data.CONTENT_URI;            //等同 Uri uri2 =Uri.parse("content://com.android.contacts/data" );            //根据刚才获得的用户的ID值来分别获取对应的数据            //获取该Uri2下的信息            // String d=ContactsContract.Contacts.Data.RAW_CONTACT_ID;这里的字符串d等同于"raw_contact_id"            Cursor cursor2 = resolver.query(uri2, null, "raw_contact_id=?", new String[]{id + ""}, null);            //获取每一个用户ID里面某些信息            while (cursor2.moveToNext()) {                //获取用户的姓名                //ContactsContract.Contacts.DISPLAY_NAME-->"display_name"                user.name = cursor2.getString(cursor2.getColumnIndex("display_name"));                // ContactsContract.Data.DATA1-->"data1"                user.phone.add(cursor2.getString(cursor2.getColumnIndex("data1")));            }            list.add(user);        }        //打印输出内容        for (int i = 0; i < list.size(); i++) {            Log.e("TAG", list.get(i).toString());        }    }    //创建一个用户信息类    class User {        long id;//用户的ID        String name;//用户的姓名        List<String> phone = new ArrayList<>();//用户的多个号码        //显示用户的数据        @Override        public String toString() {            return "User{" +                    "id=" + id +                    ", name='" + name + '\'' +                    ", phone=" + phone +                    '}';        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

       在手机联系人中,每添加一个用户信息都会对应一个ID,但是这条信息可以包含多个用户的名称或多个号码,我们只需要取其中的一部分就可以了。
       为了更好的去熟悉相关数据的操作,最好是把数据库的文件,导出来,看看数据的结构。
Android程序安装的文件都放在/data/date路径下,
系统的提供的所有ContentProvider也在这个包下:
c1
       上面的几个包名,都有providers,这是系统的ContentProvider程序包,里面包含相关的数据库文件。比如:联系人的信息在com.android.provider.contacts包内。其他的包有的是下载过得文件数据,有的是手机拨号记录数据,有的是浏览器历史记录数据等等。。。
导出手机联系人的数据库方法:
c2

使用视图工具,打开手机联系人数据库的显示界面:
c3

       上面就是ContentProvider的简单介绍和使用。
       其中自定义的ContentProvider涉及到的内容多一点,单独抽出来做例子性的总结。