7.1 内容提供器
内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时
还能保证被访数据的安全性。
目前,使用内容提供器是 Android 实现跨程序共享数据的
标准方式。
内容提供器可以选择只对哪一部分数据进行共享,从而保证隐私数据的安全。
内容提供器有两种用法:一种是
使用现有的内容提供器来读取和操作相应程序中的数据;另一种是
创建自己的内容提供器给我们程序的数据提供外部访问的接口。
7.2 访问其他程序的数据
7.2.1 ContentResolver 的基本用法
(1)
ContentResolver 类是访问内容提供器中共享数据的关键类,就类似于SQLiteDatabase类。可以通过Context的getContentResolver()方法来获取实例。其中提供了一系列的CRUD方法:insert()、update()、delete()、query(),只不过不能接收表名参数,而是一个内容
Uri对象。
(2)
内容URI,
可以清楚的表达想要访问哪个程序中的哪张表的数据,给内容提供器中的数据建立了一个唯一标识,主要有两部分组成——
权限(authority)和
路径(path)。
- 权限是为了对不同程序进行区分的,为了避免冲突,一般使用包名命名。(比如包名为com.example.app,那么就可以命名为com.example.app.provider)
- 路径则是用于对同一程序中的不同表做区分的,通常添加到权限后面。
然后,还需要在头部加上协议声明,来标识这个字符串为内容URI,标准格式如下:
content://com.example.app.provider/table_name
最后将此字符串解析为Uri对象:
Uri uri = Uri.parse("content://com.example.app.provider/table_name")
示例代码如下:
//1.获取内容提供器(当前是在MainActivity中)
ContentResolver resolver = this.getContentResolver();
//2.提供Uri对象参数
Uri uri = Uri.parse("content://com.example.app.provider/table_name");
//3.查询(其他操作如增删改类似于SQLiteDatabase的方法)
Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder);
7.2.2 读取系统联系人
(1)在activity_main.xml中加入一个ListView用来展示读取后的数据
(2)编写Activity中的代码:
public class MainActivity extends Activity {
private ListView listView;
ArrayAdapter<String> adapter;
List<String> contactList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1.首先获取ListView控件的实例
listView = (ListView) findViewById(R.id.listView);
//2.设置它的适配器
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactList);
listView.setAdapter(adapter);
//3.读取联系人数据
readContacts();
}
private void readContacts() {
Cursor cursor = null;
try {
//查询数据(ContactsContract.CommonDataKinds.Phone类已经做好了Uri封装)
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
//获取联系人姓名(对应DISPLAY_NAME常量)
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
//获取联系人手机(对应NUMBER常量)
String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//添加至集合中
contactList.add(name + "\n" + phone);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
(3)读取联系人同样要在AndroidManifest.xml中声明权限
<
uses-permission
android
:name=
"android.permission.READ_CONTACTS"
/>
7.3 创建自己的内容提供器
7.3.1 创建内容提供器
(1)要使用内容提供器,
需要自定义一个类且继承自ContentProvider类,并重写其中的6个方法,如下:
public class MyProvider extends ContentProvider {
/**
* 初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作
* 注意:只有当存在 ContentResolver 尝试访问我们程序中的数据时,内容提供器才会被初始化。
*
* @return true表示内容提供器初始化成功,false表示失败
*/
@Override
public boolean onCreate() {
return false;
}
/**
* 从内容提供器中查询数据
*
* @param uri Uri对象来确定查询哪张表
* @param projection 哪些列
* @param selection 筛选条件
* @param selectionArgs 筛选条件值
* @param sortOrder 排序
* @return Cursor对象
*/
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
/**
* 根据传入的内容 URI 来返回相应的 MIME 类型
*
* @param uri Uri对象
* @return MIME类型字符串
*/
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
/**
* 向内容提供器中添加一条数据
*
* @param uri Uri对象
* @param values ContentValues对象
* @return 一个用于表示这条新记录的 URI
*/
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
/**
* 从内容提供器中删除数据
*
* @param uri Uri对象
* @param selection 筛选条件
* @param selectionArgs 筛选条件值
* @return 被删除的行数
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
/**
* 更新内容提供器中已有的数据
*
* @param uri Uri对象
* @param values ContentValues对象
* @param selection 筛选条件
* @param selectionArgs 筛选条件值
* @return 受影响的行数
*/
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
(2)需要对Uri参数进行解析,分析出调用方期望访问的表和数据
内容URI格式主要有两种:
- 以路径结尾,如content://com.example.app.provider/table1,表示期望访问该程序table1表中的数据
- 以id结尾,如content://com.example.app.provider/table1/1,表示期望访问该程序table1表中id为1的数据
所以,用通配符的方式来匹配这两种格式的Uri:
content://com.example.app.provider/*content://com.example.app.provider/table1/#
ps:
*:匹配任意长度的任意字符#:匹配任意长度的数字
(3)通过UriMatcher类来匹配内容URI。
UriMatcher类提供了
addUri()方法,接收三个参数(
权限,路径,自定义代码),
当调用 UriMatcher 的 match()方法时,就可以将一个 Uri 对象传入,返回值是某个能够匹配这个 Uri 对象所对应的自定义代码,利用这个代码,我们就可以判断出调用方期望访问的是哪张表中的数据了。
//自定义代码
public static final int TABLE1_DIR = 0;//查询table1的所有数据代码
public static final int TABLE1_ITEM = 1;//查询table1的单条数据代码
public static final int TABLE2_DIR = 2;//查询table2的所有数据代码
public static final int TABLE2_ITEM = 3;//查询table2的单条数据代码
private static UriMatcher uriMatcher;
static {
//创建UriMatcher的实例
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//将期望匹配的添加进去
uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
//去查询table1的全部数据
break;
case TABLE1_ITEM:
//去查询table1的单条数据
break;
case TABLE2_DIR:
//去查询table2的所有数据
break;
case TABLE2_ITEM:
//去查询table2的单条数据
break;
}
return cursor;
}
Uri对象调用如下方法即可获取路径和id:
/**
* getPathSegments()方法会将内容URI权限后面的字符串以'/'分割,并存放在一个列表中
* 所以此时0下标即为路径,1下标即为id
*/
String path = uri.getPathSegments().get(0);
(4)完善getType()方法
getType()方法用于获取 Uri 对象所对应的 MIME 类型。一个内容 URI 所对应的 MIME字符串主要由三部分组分,Android 对这三个部分做了如下格式规定:
1. 必须以 vnd 开头。2. 如果内容 URI 以路径结尾,则后接 android.cursor.dir/,如果内容 URI 以 id 结尾,则后接 android.cursor.item/。3. 最后接上 vnd.<authority>.<path>。
如:
content://com.example.app.provider/table1 对应MIME类型为:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
content://com.example.app.provider/table1/1 对应MIME类型:vnd.android.cursor.item/vnd. com.example.app.provider.table1
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
default:
break;
}
return null;
}
(5)在AndroidManifest.xml中注册(application标签下)
< providerandroid :name= ".MyProvider"android :authorities= "com.jastar.databasetest.provider" ></ provider >
7.3.2 简单CRUD示例
public class DatabaseProvider extends ContentProvider {
//表名
private static final String TABLE_BOOK = "book";
private static final String TABLE_CATEGORY = "Category";
//自定义代码
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
//权限字符串
public static final String AUTHORITY = "com.jastar.databasetest.provider";
private static UriMatcher uriMatcher;
private MyDatabaseHelper dbHelper;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
return true;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
SQLiteDatabase db = dbHelper.getReadableDatabase();
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
cursor = db.query(TABLE_BOOK, projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
//获取id
String bookId = uri.getPathSegments().get(1);
cursor = db.query(TABLE_BOOK, projection, "id=?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query(TABLE_CATEGORY, projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query(TABLE_CATEGORY, projection, "id=?", new String[]{categoryId}, null, null, sortOrder);
break;
}
return cursor;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
Uri returnUri = null;
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert(TABLE_BOOK, null, values);
returnUri = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long cateId = db.insert(TABLE_CATEGORY, null, values);
returnUri = Uri.parse("content://" + AUTHORITY + "/Category/" + cateId);
break;
}
return returnUri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int deleteRows = 0;
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deleteRows = db.delete(TABLE_BOOK, null, null);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deleteRows = db.delete(TABLE_BOOK, "id=?", new String[]{bookId});
break;
case CATEGORY_DIR:
deleteRows = db.delete(TABLE_CATEGORY, null, null);
break;
case CATEGORY_ITEM:
String cateId = uri.getPathSegments().get(1);
deleteRows = db.delete(TABLE_CATEGORY, "id=?", new String[]{cateId});
break;
}
return deleteRows;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int updatedRows = 0;
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update(TABLE_BOOK, values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update(TABLE_BOOK, values, "id = ?", new String[]
{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update(TABLE_CATEGORY, values, selection,
selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update(TABLE_CATEGORY, values, "id = ?", new String[]
{categoryId});
break;
default:
break;
}
return updatedRows;
}
}