内容提供程序管理对结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。 内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。如果要访问内容提供程序中的数据,可以将应用的 Context 中的 ContentResolver 对象用作客户端来与提供程序通信。
ContentResolver 对象会与提供程序对象(即实现 ContentProvider 的类实例)通信。 提供程序对象从客户端接收数据请求,执行请求的操作并返回结果。如果自己开发的应用不打算与其他应用共享数据,则无需开发自己的提供程序。
Android 本身包括的内容提供程序可管理音频、视频、图像和个人联系信息等数据。 android.provider 软件包参考文档中列出了部分提供程序。
任何 Android 应用都可以访问这些提供程序,但会受到某些限制。
内容提供程序以一个或多个表(与在关系型数据库中找到的表类似)的形式将数据呈现给外部应用。
行表示提供程序收集的某种数据类型的实例,行中的每个列表示为实例收集的每条数据。
Android 平台的内置提供程序之一是用户字典,它会存储用户想要保存的非标准字词的拼写,如下图所示:
上图中每行表示可能无法在标准词典中找到的字词实例。 每列表示该字词的某些数据,如该字词首次出现时的语言区域。 列标题是存储在提供程序中的列名称。 要引用行的语言区域,需要引用其 locale 列。对于此提供程序,_ID 列充当由提供程序自动维护的“主键”列。
访问内容提供器中的数据,必须使用ContentResolver类
ContentResolver contentResolver = getContentResolver()
通过contentResolver实现对数据库的CRUD;ContentResolver 中的增删改查方法都是不接受表名参数的,而是使用一个Uri 参数代替,这个参数被称为内容URI。内容URI 给内容提供器中的数据建立了唯一标识符,它主要由两部分组成,权限(authority)和路径(path);前者用于区分不同的应用程序,后者用于区分同一个应用程序中不同的包名。
最标准的格式写法:
content://com.example.app.provider/table1
content://com.example.app.provider/table1/1
这就表示调用方期望访问的是com.example.app 这个应用的table1 表中id 为1 的数据。
内容URI 的格式主要就只有以上两种,以路径结尾就表示期望访问该表中所有的数据,以id 结尾就表示期望访问该表中拥有相应id 的数据。我们可以使用通配符的方式来分别匹配这两种格式的内容URI,规则如下。
1. *:表示匹配任意长度的任意字符
2. #:表示匹配任意长度的数字
所以,一个能够匹配任意表的内容URI 格式就可以写成:
content://com.example.app.provider/*
而一个能够匹配table1 表中任意一行数据的内容URI 格式就可以写成:
content://com.example.app.provider/table1/#
上表记录了query与SQL的查询对比
得到了内容URI 字符串之后,需要将它解析成Uri 对象才可以作为参数传入:
Uri uri = Uri.parse("content://com.example.app.provider/table1")//解析成Uri 对象
Cursor cursor = getContentResolver().query(
uri,
projection, // 指定查询的列名
selection, // 指定where的约束条件
selectionArgs, // where填充占位符
sortOrder); // 指定查询结果的排序方式
查询完成后返回的一个 Cursor 对象,通过移动游标的位置来遍历Cursor 的所有行.
首先配置权限
运行时权限检查
@Override
protected void onCreate(Bundle savedInstanceState) {
……
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_CONTACTS }, 1);
} else {
readContacts();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readContacts();
} else {
Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
查询
private void read() {
Cursor cursor = null;
try {
// 查询联系人数据
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != 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.add(displayName + "\n" + number);
}
adapter.notifyDataSetChanged();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
添加
private void insert(){
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
long contactid = ContentUris.parseId(resolver.insert(uri, values));
uri = Uri.parse("content://com.android.contacts/data");
values.put("raw_contact_id", contactid);
values.put(ContactsContract.Contacts.Data.MIMETYPE, "vnd.android.cursor.item/name");
values.put("data", "coco");
resolver.insert(uri, values);
values.clear();
}
删除
private void delete() {
String name = "coco";
//根据姓名求id
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver = getContext().getContext().getContentResolver();
Cursor cursor = resolver.query(uri, new String[]{ContactsContract.Contacts.Data._ID}, "display_name=?", new String[]{name}, null);
if (cursor.moveToFirst()) {
int id = cursor.getInt(0);
//根据id删除data中的相应数据
resolver.delete(uri, "display_name=?", new String[]{name});
uri = Uri.parse("content://com.android.contacts/data");
resolver.delete(uri, "raw_contact_id=?", new String[]{id + ""});
}
}
更新
private void updata() {
int id = 1;
String phone = "110";
Uri uri = Uri.parse("content://com.android.contacts/data");//对data表的所有数据操作
ContentResolver resolver = getContext().getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put("data", phone);
resolver.update(uri, values, "mimetype=? and raw_contact_id=?", new String[]{"vnd.android.cursor.item/phone_v2", id + ""});
}
继承ContentProvider类,重写增删改查方法,在方法中写增删改查数据库的代码
在清单文件中定义内容提供者的标签,注意必须要有authorities属性,这是内容提供者的主机名,功能类似地址
onCreate()
onCreate()
初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作, 返回 true 表示内容提供器初始化成功,返回 false 则表示失败。注意,只有当存在 ContentResolver 尝试访问我们程序中的数据时,内容提供器才会被初始化。需要传入getContext()参数获取上下文(这个上下文不是虚拟的上下文,是内容提供者正在运行的上下文)
query()
从内容提供器中查询数据。使用uri 参数来确定查询哪张表,projection 参数用于确 定查询哪些列,selection 和selectionArgs 参数用于约束查询哪些行,sortOrder 参数用于 对结果进行排序,查询的结果存放在Cursor 对象中返回。
insert()
向内容提供器中添加一条数据。使用uri 参数来确定要添加到的
@Override
public boolean onCreate() {
mContext = getContext();
initProviderData();
return true;
}
private void initProviderData() {
mDatabase = new Myopenhelper(mContext).getReadableDatabase();
ContentValues values = new ContentValues();
values.put("name", "Android");
mDatabase.insert(Myopenhelper.BOOK_TABLE_NAME, null, values);
values.clear();
values.put("name", "Ios");
mDatabase.insert(Myopenhelper.BOOK_TABLE_NAME, null, values);
values.clear();
values.put("name", "Html5");
mDatabase.insert(Myopenhelper.BOOK_TABLE_NAME, null, values);
values.clear();
values.put("name", "jake");
mDatabase.insert(Myopenhelper.USER_TABLE_NAME, null, values);
values.clear();
values.put("name", "coco");
mDatabase.insert(Myopenhelper.USER_TABLE_NAME, null, values);
}
创建一个其他应用,访问自定义的内容提供者,实现对数据库的插入操作
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
mDatabase.insert(table, null, values);
mContext.getContentResolver().notifyChange(uri, null);//内容观察者
return uri;
}
使用UriMatcher类实现匹配内容uri的功能
//指定多条uri
private static final String AUTHORITY = "cqupt.second.book.provider";
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
private static final int BOOK_URI_CODE = 0;
private static final int USER_URI_CODE = 1;
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
static {
URI_MATCHER.addURI(AUTHORITY, "book", BOOK_URI_CODE);
URI_MATCHER.addURI(AUTHORITY, "user", USER_URI_CODE);
}
通过Uri匹配器可以实现操作不同的表 :使用match()方法,该方法接收一个uri对象,返回自定义码
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
mDatabase.insert(table, null, values);
mContext.getContentResolver().notifyChange(uri, null);//内容观察者
return uri;
}
删除
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
int delete = mDatabase.delete(table, selection, selectionArgs);
if (delete > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return delete;
}
更新
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
int update = mDatabase.update(table, values, selection, selectionArgs);
if (update > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return update;
}
自定义的内容提供器中有一个重写父类的getType()的方法,这个方法用于获取uri对象所对应的MIME类型,一个URI所对应的MIME字符串主要由3部分组成:
/内容观察者
//1.首先获取
ContentResolver ContentResolver resolver = getContentResolver();
//2.注册内容观察者
resolver.registerContentObser(Uri.parse("content://sms"),true,observer )
//当数据库数据改变时,内容提供者会发出通知,在内容提供者的uri上注册一个内容观察者,就可以收到数据改变的通知
cr.registerContentObserver(Uri.parse("content://sms"), true, new MyObserver(new Handler()));
class MyObserver extends ContentObserver{
public MyObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
//内容观察者收到数据库发生改变的通知时,会调用此方法
@Override
public void onChange(boolean selfChange) {
}
}
在内容提供者中发通知的代码
ContentResolver cr = getContext().getContentResolver();
//发出通知,所有注册在这个uri上的内容观察者都可以收到通知
cr.notifyChange(uri, null);
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
private Context mContext;
public MyDatabaseHelper(Context context, String name,
SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
public class BookProvider extends ContentProvider {
private static final String TAG = "BookProvider";
private static final String AUTHORITY = "cqupt.second.book.provider";
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
private Context mContext;
private SQLiteDatabase mDatabase;
private static final int BOOK_URI_CODE = 0;
private static final int USER_URI_CODE = 1;
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
static {
URI_MATCHER.addURI(AUTHORITY, "book", BOOK_URI_CODE);
URI_MATCHER.addURI(AUTHORITY, "user", USER_URI_CODE);
}
@Override
public boolean onCreate() {
mContext = getContext();
initProviderData();
return true;
}
private void initProviderData() {
mDatabase = new Myopenhelper(mContext).getReadableDatabase();
ContentValues values = new ContentValues();
values.put("name", "Android");
mDatabase.insert(Myopenhelper.BOOK_TABLE_NAME, null, values);
values.clear();
values.put("name", "Ios");
mDatabase.insert(Myopenhelper.BOOK_TABLE_NAME, null, values);
values.clear();
values.put("name", "Html5");
mDatabase.insert(Myopenhelper.BOOK_TABLE_NAME, null, values);
values.clear();
values.put("name", "jake");
mDatabase.insert(Myopenhelper.USER_TABLE_NAME, null, values);
values.clear();
values.put("name", "coco");
mDatabase.insert(Myopenhelper.USER_TABLE_NAME, null, values);
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
Cursor cursor = mDatabase.query(table, projection, selection, selectionArgs, null, sortOrder, null);
return cursor;
}
private String getTableName(Uri uri) {
String tableName = null;
switch (URI_MATCHER.match(uri)) {
case BOOK_URI_CODE:
tableName = Myopenhelper.BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
tableName = Myopenhelper.USER_TABLE_NAME;
break;
}
return tableName;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
mDatabase.insert(table, null, values);
mContext.getContentResolver().notifyChange(uri, null);//内容观察者
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
int delete = mDatabase.delete(table, selection, selectionArgs);
if (delete > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return delete;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("unsupported URI:" + uri);
}
int update = mDatabase.update(table, values, selection, selectionArgs);
if (update > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return update;
}
}