Android 数据存储--- contentProvider

前言

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

ContentResolver

使用现有的内容提供器来读取和操作相应程序中的数据。

获取ContentResolver对象

getContentResolver的使用分两种情况:

一、在有Activity和Service的情况下

getContext().getContentResolver().insert(…);

  1. getContext()是获得一个上下文对象(Context),一般在四大组件中都会获取上下文对象。
  2. 在Activity和Service中,就没必要获取Context了,因为他本身就是,所以可以直接调用getContentResolver()。
  3. 在ContentProvider中,就需要先调用getContext()获取到Context,然后调用getContentResolver() 获得ContentResolver对 象,也就是,getContext().getContentResolver().

另外:
(1)getContext().getContentResolver()返回的是ContentResolver 对象,ContentResolver负责获取ContentProvider提供的数据。
(2) MainActivity.this.getContentResolver()+数据库操作 等同于 getContext().getContentResolver()+数据操作。

二、在没有Activity的情况下

例如:

 public class companyInfo{  
	public void AAA() throws Exception {  
			Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values); 
			getContentResolver().delete(scanUri, null, null);  
		} 
 } 

报错提示getContentResolver()不存在,需要通过activity或者service来实现。

这个类的AAA()方法肯定是有activity或者service调用的,所以需要写一个带有Context参数的构造方法就可以实现:

public class companyInfo{  
	 private Context context;  
	 public companyInfo(Context context){  
	 	this.context = context;  
	 }  

	public void AAA() throws Exception {  
		 Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values); 
		 context.getContentResolver().delete(scanUri, null, null);  
	 }
 }  

context.getContentResolver().query()

一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。

     public void fetchAllContacts() {  
        ContentResolver contentResolver = this.getContentResolver();  
        Cursor cursor = contentResolver.query(android.provider.ContactsContract.Contacts.CONTENT_URI,  
                null, null, null, null);  
        cursor.getCount();  
        while(cursor.moveToNext()) {  
            System.out.println(cursor.getString(cursor.getColumnIndex(android.provider.ContactsContract.Contacts._ID)));  
            System.out.println(cursor.getString(cursor.getColumnIndex(android.provider.ContactsContract.Contacts.DISPLAY_NAME)));  
        }  
        cursor.close();  
    }  

执行结果

     11-05 14:13:09.987: I/System.out(4692): 13  
    11-05 14:13:09.987: I/System.out(4692): 张三  
    11-05 14:13:09.987: I/System.out(4692): 31  
    11-05 14:13:09.987: I/System.out(4692): 李四  

解释:

ContentResolver contentResolver = this.getContentResolver();

this在这里指的是MainActivity,ContentResolver直译为内容解析器,什么东东?Android中程序间数据的共享是通过Provider/Resolver进行的。提供数据(内容)的就叫Provider,Resovler提供接口对这个内容进行解读。

在这里,系统提供了联系人的Provider,那么我们就需要构建一个Resolver来读取联系人的内容。

     Cursor cursor = contentResolver.query(android.provider.ContactsContract.Contacts.CONTENT_URI,  
                    null, null, null, null);  

根据Android文档,

public final Cursor query (Uri uri, String[] projection,String selection,String[] selectionArgs, StringsortOrder)

第一个参数,uri,rui是什么呢?好吧,上面我们提到了Android提供内容的叫Provider,那么在Android中怎么区分各个Provider?有提供联系人的,有提供图片的等等。所以就需要有一个唯一的标识来标识这个Provider,Uri就是这个标识,android.provider.ContactsContract.Contacts.CONTENT_URI就是提供联系人的内容提供者,可惜这个内容提供者提供的数据很少。

第二个参数,projection,真不知道为什么要用这个单词,这个参数告诉Provider要返回的内容(列Column),比如Contacts Provider提供了联系人的ID和联系人的NAME等内容,如果我们只需要NAME,那么我们就应该使用:

     Cursor cursor = contentResolver.query(android.provider.ContactsContract.Contacts.CONTENT_URI,  
        new String[]{android.provider.ContactsContract.Contacts.DISPLAY_NAME}, null, null, null);  

当然,下面打印的你就只能显示NAME了,因为你返回的结果不包含ID。用null表示返回Provider的所有内容(列Column)。

第三个参数,selection,设置条件,相当于SQL语句中的where。null表示不进行筛选。如果我们只想返回名称为张三的数据,第三个参数应该设置为:

     Cursor cursor = contentResolver.query(android.provider.ContactsContract.Contacts.CONTENT_URI,  
        new String[]{android.provider.ContactsContract.Contacts.DISPLAY_NAME},  
        android.provider.ContactsContract.Contacts.DISPLAY_NAME + "='张三'", null, null);  

结果:

11-05 15:30:32.188: I/System.out(10271): 张三 

第四个参数,selectionArgs,这个参数是要配合第三个参数使用的,如果你在第三个参数里面有?,那么你在selectionArgs写的数据就会替换掉?,

     Cursor cursor = contentResolver.query(android.provider.ContactsContract.Contacts.CONTENT_URI,  
        new String[]{android.provider.ContactsContract.Contacts.DISPLAY_NAME},  
        android.provider.ContactsContract.Contacts.DISPLAY_NAME + "=?",  
                    new String[]{"张三"}, null);  

效果和上面一句的效果一样。

第五个参数,sortOrder,按照什么进行排序,相当于SQL语句中的Order by。如果想要结果按照ID的降序排列:

 Cursor cursor = contentResolver.query(android.provider.ContactsContract.Contacts.CONTENT_URI,  
                null, null,null, android.provider.ContactsContract.Contacts._ID + " DESC"); 

ContentProvider

ContentProvider(内容提供者)是 Android 的四大组件之一,管理 Android 以结构化方式存放的数据,以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。

Android 的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。但一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,就会用到 ContentProvider。而且 Android 为常见的一些数据提供了默认的 ContentProvider(包括音频、视频、图片和通讯录等)。
在这里插入图片描述
要实现与其他的 ContentProvider 通信首先要查找到对应的 ContentProvider 进行匹配。Android 中 ContenProvider 借助 ContentResolver 通过 Uri 与其他的 ContentProvider 进行匹配通信。

URI(Uniform Resource Identifier)

其它应用可以通过 ContentResolver 来访问 ContentProvider 提供的数据,而 ContentResolver 通过 uri 来定位自己要访问的数据,所以我们要先了解 uri。URI(Universal Resource Identifier)统一资源定位符,如果您使用过安卓的隐式启动就会发现,在隐式启动的过程中我们也是通过 uri 来定位我们需要打开的 Activity 并且可以在 uri 中传递参数。

URI 为系统中的每一个资源赋予一个名字,比方说通话记录。每一个 ContentProvider 都拥有一个公共的 URI,用于表示 ContentProvider 所提供的数据。URI 的格式如下:

// 规则
[scheme:][//host:port][path][?query]
// 示例
content://com.wang.provider.myprovider/tablename/id:
  1. 标准前缀(scheme)——content://,用来说明一个Content Provider控制这些数据;

  2. URI 的标识 (host:port)—— com.wang.provider.myprovider,用于唯一标识这个 ContentProvider,外部调用者可以根据这个标识来找到它。对于第三方应用程序,为了保证 URI 标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的authorities属性中说明,一般是定义该 ContentProvider 的包.类的名称;

  3. 路径(path)——tablename,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;

  4. 记录ID(query)——id,如果URI中包含表示需要获取的记录的 ID,则返回该id对应的数据,如果没有ID,就表示返回全部;

对于第三部分路径(path)做进一步的解释,用来表示要操作的数据,构建时应根据实际项目需求而定。如:

1.操作tablename表中id为11的记录,构建路径:/tablename/11;

2.操作tablename表中id为11的记录的name字段:tablename/11/name;

3.操作tablename表中的所有记录:/tablename;

4.操作来自文件、xml或网络等其他存储方式的数据,如要操作xml文件中tablename节点下name字段:/ tablename/name;

若需要将一个字符串转换成Uri,可以使用Uri类中的parse()方法,如:

Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename")

再来看一个例子:

http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack

uri 的各个部分在安卓中都是可以通过代码获取的,下面我们就以上面这个 uri 为例来说下获取各个部分的方法:

1.getScheme():获取 Uri 中的 scheme 字符串部分,在这里是 http

2.getHost():获取 Authority 中的 Host 字符串,即 www.baidu.com

3.getPost():获取 Authority 中的 Port 字符串,即 8080

4.getPath():获取 Uri 中 path 部分,即 wenku/jiatiao.html

5.getQuery():获取 Uri 中的 query 部分,即 id=15&name=du

MIME

MIME 是指定某个扩展名的文件用一种应用程序来打开,就像你用浏览器查看 PDF 格式的文件,浏览器会选择合适的应用来打开一样。Android 中的工作方式跟 HTTP 类似,ContentProvider 会根据 URI 来返回 MIME 类型,ContentProvider 会返回一个包含两部分的字符串。MIME 类型一般包含两部分,如:

text/html
text/css
text/xml
application/pdf

分为类型和子类型,Android 遵循类似的约定来定义MIME类型,每个内容类型的 Android MIME 类型有两种形式:多条记录(集合)和单条记录。

  1. 集合记录(dir):
vnd.android.cursor.dir/自定义 
  1. 单条记录(item):
vnd.android.cursor.item/自定义 

nd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写。

在使用 Intent 时,会用到 MIME,根据 Mimetype 打开符合条件的活动。

UriMatcher

Uri 代表要操作的数据,在开发过程中对数据进行获取时需要解析 Uri,Android 提供了两个用于操作 Uri 的工具类,分别为 UriMatcher 和 ContentUris 。掌握它们的基本概念和使用方法,对一个 Android 开发者来说是一项必要的技能。

UriMatcher 类用于匹配 Uri,它的使用步骤如下:

  1. 将需要匹配的Uri路径进行注册,代码如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路径,返回匹配码为1
sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);
//如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路径,返回匹配码为2
sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);

此处采用 addURI 注册了两个需要用到的 URI;注意,添加第二个 URI 时,路径后面的 id 采用了通配符形式 “#”,表示只要前面三个部分都匹配上了就 OK。

  1. 注册完需要匹配的 Uri 后,可以使用 sMatcher.match(Uri) 方法对输入的 Uri 进行匹配,如果匹配就返回对应的匹配码,匹配码为调用 addURI() 方法时传入的第三个参数。
switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {
    case 1:
        //match 1, todo something
        break;
    case 2
        //match 2, todo something
        break;
    default:
        //match nothing, todo something
        break;
}

ContentUris

ContentUris 类用于操作 Uri 路径后面的 ID 部分,它有两个比较实用的方法:withAppendedId(Uri uri, long id) 和 parseId(Uri uri)。

  1. withAppendedId(Uri uri, long id) 用于为路径加上 ID 部分:
Uri uri = Uri.parse("content://cn.scu.myprovider/user")

//生成后的Uri为:content://cn.scu.myprovider/user/7
Uri resultUri = ContentUris.withAppendedId(uri, 7); 
  1. parseId(Uri uri) 则从路径中获取 ID 部分:
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")

//获取的结果为:7
long personid = ContentUris.parseId(uri);

ContentProvider 主要方法

ContentProvider 是一个抽象类,如果我们需要开发自己的内容提供者我们就需要继承这个类并复写其方法,需要实现的主要方法如下:

1. public boolean onCreate():在创建 ContentProvider 时使用

2. public Cursor query():用于查询指定 uri 的数据返回一个 Cursor

3. public Uri insert():用于向指定uri的 ContentProvider 中添加数据

4. public int delete():用于删除指定 uri 的数据

5. public int update():用户更新指定 uri 的数据

6. public String getType():用于返回指定的 Uri 中的数据 MIME 类型
7. 

数据访问的方法 insert,delete 和 update 可能被多个线程同时调用,此时必须是线程安全的。

  1. 如果操作的数据属于集合类型,那么 MIME 类型字符串应该以 vnd.android.cursor.dir/ 开头,
要得到所有 tablename 记录: Uri 为 content://com.wang.provider.myprovider/tablename,那么返回的MIME类型字符串应该为:vnd.android.cursor.dir/table。
  1. 如果要操作的数据属于非集合类型数据,那么 MIME 类型字符串应该以 vnd.android.cursor.item/ 开头,
要得到 id 为 10 的 tablename 记录,Uri 为 content://com.wang.provider.myprovider/tablename/10,那么返回的 MIME 类型字符串为:vnd.android.cursor.item/tablename 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值