Android笔记---四大组件之Content Provider内容提供者详解

时间:2021-07-18 09:22:32

ContentProvider(内容提供者)是android中的四大组件之一,主要用于在不同的应用程序之间实现数据共享的功能,很多系统自带应用,比如联系人信息,图片库,音频库等应用,为了对其他应用暴露数据,就使用了ContentProvider机制,所以在遇到获取联系人信息,图片库,音频库等需求的时候,我们也需要用到ContentProvider,另外,如果我们想让其他的应用使用我们自己程序内的数据,可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用咱们应用的文件、数据库内存储的信息。

我们先看下ContentProvider中需要掌握的知识点:
1.URI字符串
2.ContentResolve类
3.UriMatcher类
4.ContentUris类

ContentProvider是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”,外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样有增删改查的方式,不过在方法参数上稍微有一些区别,所以我们首先需要找到我们的“数据表”,而这是通过一个URI字符串来确定的。

URI字符串由以下三部分组成:
1.scheme: 规定为content://
2.主机名(Authority): 用于唯一标识这个ContentProvider,外部调用可以根据这个标识来找到它,一般为了避免冲突, 都会采用程序包名的方式来进行命名
3.路径(path): 用于对同一应用程序中不同的表做区分的, 添加在权限的后面,eg:
要操作person表中id为10的记录,可以构建这样的路径:/person/10
要操作person表中id为10的记录的name字段, person/10/name
要操作person表中的所有记录,可以构建这样的路径:/person
当然要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下:
要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name

在得到了内容URI字符串之后, 我们还需要将它解析成 Uri 对象才可以作为参数传入,只需要调用 Uri.parse()方法, 就可以将内容URI字符串解析成 Uri 对象了,加入我们需要对程序中为person的表进行操作,就可以先得到以下这样一个uri对象:
Uri uri = Uri.parse("content://com.example.app.provider/person")

对到了uri对象之后我们就需要调用相应的方法对其进行操作,这样就需要借助于 ContentResolve 类, 可以通过 Context 中的 getContentResolver()方法获取到该类的实例,然后对数据进行增删改查。

以下通过两种情况进行分析:
1.获取系统应用程序的数据(以获取联系人电话方式为例)
2.本程序创建ContentProvider定义一个对外开放的接口将数据提供给其他应用程序调用。

1.获取系统应用程序的数据(以获取联系人电话方式为例)

获取系统的内容提供者的程序比较简单:
首先添加两条联系人已经电话记录:
Android笔记---四大组件之Content Provider内容提供者详解
在布局文件中增加一个ListView控件来展示获取的联系人,当然也有别的方式来显示联系人的信息,直接用TextView也可以。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent" >

</ListView>
</RelativeLayout>

接下来给出Activity中的代码:

package com.example.androidstudycontentprovider;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
private ListView contactsView;

//定义一个适配器
ArrayAdapter<String> adapter;
//定义一个ArrayList数组容器来存放得到的联系人数据
List<String> contactsList = new ArrayList<String>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contactsView = (ListView) findViewById(R.id.contacts_view);

//为适配器添加布局R.layout.simple_list_item_1(android自带的布局)以及联系人信息contactsList
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, contactsList);
//给ListView设置适配器
contactsView.setAdapter(adapter);
//调用readContacts()方法
readContacts();
}

private void readContacts() {
Cursor cursor = null;
try {
//查询联系人数据,ContactsContract.CommonDataKinds.Phone类已经帮我们做好了封装
//提供了一个 CONTENT_URI 常量,而这个常量就是使用 Uri.parse()方法解析出来的结果,所以可以直接就用
cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
null, null, null);

while (cursor.moveToNext()) {
// 获取联系人姓名
String displayName = cursor
.getString(cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
// 获取联系人手机号
String number = cursor
.getString(cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//在contactsList容器添加需要显示的数据
contactsList.add(displayName + " " + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
//关闭cursor
cursor.close();
}
}
}
}

最后在AndroidManifest.xml文件中声明可以读取联系*限就可以了:

<uses-permission android:name="android.permission.READ_CONTACTS" />

程序运行的结果如下:
Android笔记---四大组件之Content Provider内容提供者详解

2.本程序创建ContentProvider定义一个对外开放的接口将数据提供给其他应用程序调用。

对于这种情况,我们需要先掌握以下内容:
Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris ,所以我们需要先掌握下这两个类:

1.UriMatcher

UriMatcher 类主要用于匹配Uri,UriMatcher中提供了一个 addURI()方法,这个方法接收三个参数,可以分别把权限、路径和一个自定义代码传进去。这样, 当调用 UriMatcher 的 match()方法时,就可以将一个 Uri 对象传入, 返回值是某个能够匹配这个 Uri 对象所对应的自定义代码, 利用这个代码, 我们就可以判断出调用方期望访问的是哪张表中的数据了。
使用方法如下。
首先第一步,初始化:

//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

第二步注册需要的Uri, 传入三个参数:

//*:表示匹配任意长度的任意字符
//#:表示匹配任意长度的数字
//所以,一个能够匹配任意表的内容 URI 格式就可以写成: content://com.dxy.provider/*
//而一个能够匹配 table1 表中任意一行数据的内容 URI格式就可以写成: content://com.dxy.provider/table1/#

//如果match()方法匹配content://com.dxy.provider/person路径,返回匹配码为PEOPLE
matcher.addURI("com.dxy.provider", "people", PEOPLE);
matcher.addURI("com.dxy.provider", "person/#", PEOPLE_ID);

第三步,与已经注册的Uri进行匹配:

Uri uri = Uri.parse("content://" + "com.dxy.provider" + "/people");  
int match = matcher.match(uri);
switch (match)
{
case PEOPLE:
//返回值是某个能够匹配这个 Uri 对象所对应的自定义代码
return "com.dxy.provider/people";
case PEOPLE_ID:
return "com.dxy.provider/people";
default:
return null;
}

2.ContentUris

ContentUris 类用于获取Uri路径后面的ID部分, 它有两个比较实用的方法:
1.withAppendedId(uri, id)用于为路径加上ID部分:

Uri uri = Uri.parse("content://com.dxy.provider");
//通过withAppendedId方法,为该Uri加上ID
//最后resultUri为: content://com.dxy.provider/people/10
Uri resultUri = ContentUris.withAppendedId(uri, 10);

2.parseId(uri)方法用于从路径中获取ID部分:

Uri uri = Uri.parse("content://com.dxy.provider/people/10")  
//可以从路径中或者id,最后personid 为 :10
long personid = ContentUris.parseId(uri);

接下来我们看下ContentResolver类提供的四个方法对uri对象进行增删改查的操作:
1. public Uri insert(Uri uri, ContentValues values)
该方法用于往ContentProvider添加数据,用法如下:

ContentResolver resolver =  getContentResolver();
Uri uri = Uri.parse("content://com.example.app.provider/person");
//实例化一个ContentValues类并且插入数据,ContentValues类也是负责存储一些键值对
ContentValues values = new ContentValues();
values.put("name", "itcast");
values.put("age", 25);
resolver.insert(uri, values);

2. public int delete(Uri uri, String selection, String[] selectionArgs)
该方法用于从ContentProvider删除数据,用法如下:

ContentResolver resolver =  getContentResolver();
Uri uri = Uri.parse("content://com.dxy.provider/person");
//通过withAppendedId方法,为该Uri加上ID
//最后deleteIdUri为: content://com.dxy.provider/people/10
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);

3. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
该方法用于更新ContentProvider中的数据,用法如下:

ContentResolver resolver =  getContentResolver();
Uri uri = Uri.parse("content://com.dxy.provider/person");
//把id为1的记录的name字段值更改新为liming
ContentValues updateValues = new ContentValues();
updateValues.put("name", "liming");
//通过withAppendedId方法,为该Uri加上ID
//最后updateIdUri为: content://com.dxy.provider/people/1
Uri updateIdUri = ContentUris.withAppendedId(uri, 1);
resolver.update(updateIdUri, updateValues, null, null);

4. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
该方法用于从ContentProvider中获取数据.
第一个参数为Uri,代表指定查询某个应用程序下的某一张表,第二个参数指定查询的列名,第三个参数指定 where 的约束条件,第四个参数为where中的占位符提供具体的值,第五个参数指定查询结果的排序方式。

ContentResolver resolver =  getContentResolver();
Uri uri = Uri.parse("content://com.dxy.provider/person");
//获取person表中所有记录
Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
while(cursor.moveToNext()){
Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));
}

具体实例代码这里不给出,可以参考android之内容提供者解析