在日常的开发中,对ContentProvider的接触比较少,总体的认识就是一个用于提供分享信息的渠道(可以是应用内部也可以是不同的App之间),它们通过唯一的uri进行通信。因为使用的不是特别多,所以这里也只是简单介绍一下ContentProvider的使用。主要有下面几点:
1. 创建自己的ContentProvider对外部提供数据;
2. 使用ContentResolver获取外部App提供的数据;
3. 操作系统的联系人;
相关知识简介
Uri(Uniform Resource Identifier) 统一资源标识符
在ContentProvider中Uri代表了要操作的数据的路径,有了Uri,系统就知道这个Uri对应的数据由哪个ContentProvider提供
一般格式:content:// authority/标识(可选)
UriMatcher类
在使用Uri的时候你需要将Uri对应到具体对某个数据库的某种操作上去,UriMatcher类就是Android提供用于将Uri跟code(自定义的常量)对应起来的一个类,有了UriMatcher就 可以轻松地对应Uri跟code,然后根据不同的code执行不同的操作。
Cursor类
数据库游标,可以通过moveToNext等方法遍历元素。
一、创建ContentProvider对外提供服务
这里使用对外提供联系人表为例(这里省略了具体的数据库表操作,因为我们主要是为了讲ContentProvider,我们只是简单地进行了Log操作证明别的应用调用到了数据库操作的部分)。
创建ContentProvider的Authority(一般为包名,要保证Authority在系统中的唯一性!),这个Authority就是Uri中的authority;
private static final String AUTHORITY = "com.scut.jayme";
新建一个ContentProvider类(这里就叫ContactProvider)
ContactProvider要继承自ContentProvider并且实现其中的虚函数。
package com.scut.jayme;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* 提供数据的示例
* 对外部提供用户的联系人表
*
* Created by jayme on 15/12/19.
*/
public class ContactProvider extends ContentProvider {
private static final String AUTHORITY = "com.scut.jayme";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
public static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
public static final int USER_CONTACT = 0;
public static final int USER_CONTACT_ID = 1;
/**
* 这里使用静态初始化块是为了让 sUriMatcher 及时得到初始化
*/
static {
/** UriMatcher可以将uri跟code建立关联,也可以把一个uri转化为一个code,code是自定义的用于区分操作的int变量 */
sUriMatcher.addURI(AUTHORITY, "contact", USER_CONTACT);
sUriMatcher.addURI(AUTHORITY, "contact/#", USER_CONTACT_ID); //#代表一个具体的数值
}
private static final String TAG = "contact";
private static final String MIME_TYPE_ITEM = "com.scut.jayme.item/contact";
private static final String MIME_TYPE_DIR = "com.scut.jayme.dir/contact";
@Override
public boolean onCreate() {
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.i(TAG, "查询 contact表");
/** 这里讲具体的Uri转换为code */
int operationCode = sUriMatcher.match(uri);
switch (operationCode){
case USER_CONTACT:
Log.i(TAG, "查询到了 contact表");
break;
case USER_CONTACT_ID:
int id = Integer.parseInt(uri.getPathSegments().get(1));
Log.i(TAG, "查询到了 contact表, id = " + id);
break;
default: Log.i(TAG, "Invalid request: " + uri);
}
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
try {
Integer.parseInt(uri.getPathSegments().get(1));
return MIME_TYPE_ITEM;
} catch (NumberFormatException ex) {
return MIME_TYPE_DIR;
}
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
/** 这里讲具体的Uri转换为code */
int operationCode = sUriMatcher.match(uri);
switch (operationCode) {
case USER_CONTACT:
Log.d(TAG, "成功在 contact表 中插入一条数据");
//Todo 执行插入数据库操作
break;
default:
Log.e(TAG, "Invalid request: " + uri);
}
return null;
}
@Override
public int delete(@NonNull Uri uri, String where, String[] selectionArgs) {
/** 这里讲具体的Uri转换为code */
int operationCode = sUriMatcher.match(uri);
//Todo 执行删除数据库操作
switch (operationCode) {
case USER_CONTACT:
if (where == null) {
Log.d(TAG, "删除 contact表 中的所有元素");
} else {
Log.d(TAG, "删除 contact表 重元素, where = " + where);
}
break;
case USER_CONTACT_ID:
int id = Integer.parseInt(uri.getPathSegments().get(1));
Log.d(TAG, "删除 contact表 中元素, id = " + id);
break;
default:
Log.e(TAG, "Invalid request: " + uri);
}
return 1;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String where, String[] selectionArgs) {
/** 这里讲具体的Uri转换为code */
int operationCode = sUriMatcher.match(uri);
//Todo 执行更新数据库操作
switch (operationCode) {
case USER_CONTACT:
if (where == null) {
Log.e(TAG, "更新失败");
} else {
Log.d(TAG, "更新成功, where = " + where);
}
break;
case USER_CONTACT_ID:
int id = Integer.parseInt(uri.getPathSegments().get(1));
Log.d(TAG, "更新成功, id = " + id);
break;
default:
Log.e(TAG, "Invalid request: " + uri);
}
return 1;
}
}
在AndroidMainfest.xml文件中配置Provider
把上面这段代码加入到application下就可以了。
二、使用ContentResolver获取数据
要获取数据有两部,第一步是知道提供数据ContentProvider的Uri,然后就是从Activity中调用getContentResolver获取到一个ContentResolver对象,然后就可以对数据进行处理了,具体可以仔细看代码中的注释。
package com.jayme.aboutcontentresolver;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
/**
* 测试ContentResolver操作别的App提供的数据
*
* Created by Jayme on 16/01/23
*/
public class MainActivity extends Activity {
/** 数据来源的Uri */
public static final Uri CONTACT_URI
= Uri.parse("content://com.scut.jayme/contact");
public static final Uri CONTACT_ITEM_URI
= Uri.parse("content://com.scut.jayme/contact/1");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 查询 CONTACT表
* 返回一个Cursor对象即可获取到表中数据
* Todo: 利用返回的Cursor对象获取数据
*/
Cursor cursor = getContentResolver().query(CONTACT_URI, null, null, null, null);
/**
* 往 CONTACT表中插入一条数据
* ContentValues是一个保存键值对的对像
* 因为是在测试,所以values也是随便写的,记得要传一个ContentValues对象即可
*/
ContentValues values = new ContentValues(1);
values.put("name", "Jayme");
getContentResolver().insert(CONTACT_URI, values);
/**
* 删除 CONTACT表 中id为1的项
*/
getContentResolver().delete(CONTACT_ITEM_URI, null, null);
/**
* 更新 CONTACT表中id为1的项
*/
getContentResolver().update(CONTACT_ITEM_URI, values, null, null);
}
}
三、操作Android提供的联系人表
Demo截图
获取所有联系人的信息
首先需要获取到一个ContentResolver对象,这里现在onCreate中获取到了(也就是说把下面那句代码放到onCreate里)
mContentResolver = getContentResolver();
然后利用上面的Uri查询获得一个Cursor对象就可以获取到所有联系人的信息,这里展示方式为在Log中显示。
先获取到联系人的id跟name,然后再通过联系人的id进行了一次查询操作获取到了联系人的号码(因为号码可能有多个, 所以需要遍历),切记不要传错Uri!!!
/**
* 显示所有手机联系人
*/
private void showContacts(){
Cursor cursor = mContentResolver.query(Contacts.CONTENT_URI, null, null, null, null);
/** 这里遍历可以获取到所有联系人的 id 跟 name */
while(cursor != null && cursor.moveToNext()){
String id = cursor.getString(cursor.getColumnIndex(Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(Contacts.DISPLAY_NAME));
/** 再进行一次查询获取对应联系人的电话号码 */
Cursor phoneCursor = mContentResolver.query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + "=" + id, null, null);
StringBuilder sb = new StringBuilder("contact_id = ").append(id).append(" " + name);
while(phoneCursor != null && phoneCursor.moveToNext()){
sb.append(" " + phoneCursor.getString(phoneCursor.getColumnIndex(Phone.NUMBER)));
}
/** 关闭Cursor对象 */
if(null != phoneCursor){
phoneCursor.close();
}
/** 显示信息 */
Log.i(TAG, sb.toString());
}
/** 关闭Cursor对象 */
if(null != cursor){
cursor.close();
}
}
插入一条联系人的记录有两步,首先要通过RawContacts的Uri生成一条联系人纪录,然后再通过Data的Uri对刚刚生成记录进行信息的修改(依据RawContacts生成记录的Id)
/**
* 增加一条数据
*/
private void addContact(String name, String phone){
/** 先新建一条联系人数据并获取它对应的Id */
ContentValues values = new ContentValues();
Uri newContactUri = mContentResolver.insert(RawContacts.CONTENT_URI, values);
long contactId = ContentUris.parseId(newContactUri);
/** 添加不同信息使用的是同一个Uri,具体操作的区分是通过 MIMETYPE 来区别的 */
/** 添加刚才新建联系人的姓名 */
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
values.put(StructuredName.GIVEN_NAME, name);
mContentResolver.insert(Data.CONTENT_URI, values);
values.clear();
/** 添加刚才新建联系人的电话 */
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
values.put(Phone.NUMBER, phone);
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
mContentResolver.insert(Data.CONTENT_URI, values);
values.clear();
/** 添加刚才新建联系人的邮箱 */
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
values.put(Email.DATA, "[email protected]");
values.put(Email.TYPE, Email.TYPE_WORK);
mContentResolver.insert(Data.CONTENT_URI, values);
}
/**
* 查找电话对应联系人
* @param phoneNumber 对应电话
*/
private void queryContact(String phoneNumber){
Uri uri = Uri.parse(Phone.CONTENT_URI + "/filter/" + phoneNumber);
Cursor cursor = mContentResolver.query(uri, new String[]{ContactsContract.Data.DISPLAY_NAME}, null, null, null);
if(null != cursor && cursor.moveToFirst()){
showToast("电话 " + phoneNumber + " 对应联系人是 " + cursor.getString(0));
}else{
showToast("没有找到对应的联系人");
}
if(null != cursor){
cursor.close();
}
}
/**
* 删除对应姓名的联系人
* @param name 联系人姓名
*/
private void deleteContact(String name){
Cursor cursor = mContentResolver.query(RawContacts.CONTENT_URI, new String[]{ContactsContract.Data._ID}, "display_name=?", new String[]{name}, null);
if(null != cursor && cursor.moveToFirst()){
//获取联系人对应Id
int id = cursor.getInt(0);
//根据id删除data中的相应数据
mContentResolver.delete(RawContacts.CONTENT_URI, "display_name=?", new String[]{name});
mContentResolver.delete(Data.CONTENT_URI, "raw_contact_id=?", new String[]{id + ""});
showToast("delete " + name + " success");
}
}
package com.jayme.contacts;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity implements View.OnClickListener{
private static final String TAG = "tag_contacts";
private ContentResolver mContentResolver;
private Button mShowContactsButton;
private Button mInsertButton;
private Button mQueryButton;
private Button mDeleteButton;
private EditText mInsertNameEditText;
private EditText mInsertPhoneEditText;
private EditText mQueryNameEditText;
private EditText mDeleteNameEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContentResolver = getContentResolver();
findViews();
initListeners();
}
/**
* 绑定控件
*/
private void findViews(){
mShowContactsButton = (Button) findViewById(R.id.main_btn_show_contacts);
mInsertButton = (Button) findViewById(R.id.main_btn_add);
mDeleteButton = (Button) findViewById(R.id.main_btn_delete);
mQueryButton = (Button) findViewById(R.id.main_btn_query);
mInsertNameEditText = (EditText) findViewById(R.id.main_et_add_name);
mInsertPhoneEditText = (EditText) findViewById(R.id.main_et_add_phone);
mQueryNameEditText = (EditText) findViewById(R.id.main_et_query_phone);
mDeleteNameEditText = (EditText) findViewById(R.id.main_et_delete_name);
}
/**
* 为按钮设置监听
*/
private void initListeners(){
mShowContactsButton.setOnClickListener(this);
mDeleteButton.setOnClickListener(this);
mInsertButton.setOnClickListener(this);
mQueryButton.setOnClickListener(this);
}
/**
* 显示所有手机联系人
*/
private void showContacts(){
Cursor cursor = mContentResolver.query(Contacts.CONTENT_URI, null, null, null, null);
/** 这里遍历可以获取到所有联系人的 id 跟 name */
while(cursor != null && cursor.moveToNext()){
String id = cursor.getString(cursor.getColumnIndex(Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(Contacts.DISPLAY_NAME));
/** 再进行一次查询获取对应联系人的电话号码 */
Cursor phoneCursor = mContentResolver.query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + "=" + id, null, null);
StringBuilder sb = new StringBuilder("contact_id = ").append(id).append(" " + name);
while(phoneCursor != null && phoneCursor.moveToNext()){
sb.append(" " + phoneCursor.getString(phoneCursor.getColumnIndex(Phone.NUMBER)));
}
/** 关闭Cursor对象 */
if(null != phoneCursor){
phoneCursor.close();
}
/** 显示信息 */
Log.i(TAG, sb.toString());
}
/** 关闭Cursor对象 */
if(null != cursor){
cursor.close();
}
}
/**
* 查找电话对应联系人
* @param phoneNumber 对应电话
*/
private void queryContact(String phoneNumber){
Uri uri = Uri.parse(Phone.CONTENT_URI + "/filter/" + phoneNumber);
Cursor cursor = mContentResolver.query(uri, new String[]{ContactsContract.Data.DISPLAY_NAME}, null, null, null);
if(null != cursor && cursor.moveToFirst()){
showToast("电话 " + phoneNumber + " 对应联系人是 " + cursor.getString(0));
}else{
showToast("没有找到对应的联系人");
}
if(null != cursor){
cursor.close();
}
}
/**
* 增加一条数据
*/
private void addContact(String name, String phone){
/** 先新建一条联系人数据并获取它对应的Id */
ContentValues values = new ContentValues();
Uri newContactUri = mContentResolver.insert(RawContacts.CONTENT_URI, values);
long contactId = ContentUris.parseId(newContactUri);
/** 添加不同信息使用的是同一个Uri,具体操作的区分是通过 MIMETYPE 来区别的 */
/** 添加刚才新建联系人的姓名 */
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
values.put(StructuredName.GIVEN_NAME, name);
mContentResolver.insert(Data.CONTENT_URI, values);
values.clear();
/** 添加刚才新建联系人的电话 */
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
values.put(Phone.NUMBER, phone);
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
mContentResolver.insert(Data.CONTENT_URI, values);
values.clear();
/** 添加刚才新建联系人的邮箱 */
values.put(Data.RAW_CONTACT_ID, contactId);
values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
values.put(Email.DATA, "[email protected]");
values.put(Email.TYPE, Email.TYPE_WORK);
mContentResolver.insert(Data.CONTENT_URI, values);
}
/**
* 删除对应姓名的联系人
* @param name 联系人姓名
*/
private void deleteContact(String name){
Cursor cursor = mContentResolver.query(RawContacts.CONTENT_URI, new String[]{ContactsContract.Data._ID}, "display_name=?", new String[]{name}, null);
if(null != cursor && cursor.moveToFirst()){
//获取联系人对应Id
int id = cursor.getInt(0);
//根据id删除data中的相应数据
mContentResolver.delete(RawContacts.CONTENT_URI, "display_name=?", new String[]{name});
mContentResolver.delete(Data.CONTENT_URI, "raw_contact_id=?", new String[]{id + ""});
showToast("delete " + name + " success");
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.main_btn_show_contacts:
showToast("请在控制台查看联系人输出");
new Thread(new Runnable() {
@Override
public void run() {
showContacts();
}
}).start();
break;
case R.id.main_btn_add:
String addName = mInsertNameEditText.getText().toString();
String addPhone = mInsertPhoneEditText.getText().toString();
if(!"".equals(addName) && !"".equals(addPhone)){
addContact(addName, addPhone);
showToast("新建联系人成功");
}else {
showToast("联系人姓名跟手机均不能为空");
}
break;
case R.id.main_btn_delete:
String deleteName = mDeleteNameEditText.getText().toString();
if(!"".equals(deleteName)){
deleteContact(deleteName);
}else {
showToast("删除联系人不能为空");
}
break;
case R.id.main_btn_query:
String queryPhone = mQueryNameEditText.getText().toString();
if(!"".equals(queryPhone)){
queryContact(queryPhone);
}else{
showToast("查找联系人不能为空");
}
break;
default:
break;
}
}
/**
* 显示Toast信息
* @param message 要显示的信息
*/
protected void showToast(String message){
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
总结:由于本人用ContentProvider用的也很少,所以可能有些地方写的不是很准确,如果有错的话希望大家提出来,上面说的三个demo都可以去我的资源那里下载Android Studio源文件。