今天在网上看到一篇文章。是关于金庸的《天龙八部》中段誉、乔峰、慕容复三个人,慕容复虽然会天下很多门派的功夫,却打不过段誉时灵时不灵的六脉神剑,我们再看看乔峰,只要使出降龙十八掌,便可以打败很多武林豪杰。我说出这个例子是想表达“专”和“广”的问题。其实工作三年多以来我也一直有这个毛病。总想什么都要会,到头来什么都不深的局面,就像慕容复。所以,我们应该是先在某一个领域很深入,然后由深入广。2015年我想最主要的经历都放在android研发上边,力求技术上更多的精进。参与更多的开源项目。啰嗦了这么多,只是发发感慨而已。言归正传吧。
其实,我在项目中使用Content Provider并不多,只不过想通过博客的方式记录一下我学习的笔记。以便在以后的工作上使用时,能够很快拾起。
比如我想给其他的应用程序提供数据,那么我完全可以通过URI方式给出,也就是Content Provider。因此,我们就需要自己定义一个类去继承 ContentProvider,那么我们就简单的介绍一下其中的方法吧。
package com.example.contentproviderdemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
public class MyProvider extends ContentProvider {
@Override
public boolean onCreate() {
// 只有当第一次ContentResolver对象进行数据操作时,才会执行此方法.
// 一般在此方法进行一些初始化的操作。初始化成功返回true,失败返回false
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// 可以理解成对应的sql语句 select 列 ... from 表名 where 条件
// uri 可以指定某一个表
// 返回一个游标
return null;
}
@Override
public String getType(Uri uri) {
// 根据uri返回mime类型
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// 插入数据 对应sql语句 insert into 表(列...) values(值 ...)
// 返回插入数据对应的URI
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 删除数据 delete from 表 where 条件
// 返回删除的行数
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// 更新数据 update 表 set 列 = 值 .... where 条件
// 返回更新数据的行数
return 0;
}
}
按照相应的要求重写方法即可。
那我们在回顾一下URI的格式吧。content://权限/路径,比如content://com.example.contentproviderdemo.provider/table1 其中table1表示表名称。还有就是URI中使用的通配符。
*:表示匹配任意长度的任意字符。比如:content://com.example.contentproviderdemo.provider/* 代表匹配任意表的任意数据。
#:表示匹配任意长度的数字。比如:content://com.example.contentproviderdemo.provider/table1/# 代表匹配table1表中某一个id的数据。
上边有一个getType方法需要重点介绍一下。Android中对URI对应MIME类型做了相应的规定。具体的MIME由三个部分组成的。
1. 必须是以vnd开头。
2. 如果内容URI是以路径结尾,后接android.cursor.dir/。如果是以id结尾的话,后接android.cursor.item/。
3.最后接上vnd.<权限>.<路径>
比如:content://com.example.contentproviderdemo.provider/table1对应的mime类型为:vnd.android.cursor.dir/vnd.com.example.contentproviderdemo.provider.table1。
content://com.example.contentproviderdemo.provider/table1/#对应的mime类型为:vnd.android.cursor.item/vnd.com.example.contentproviderdemo.provider.table1。
根据我以上介绍的内容,我们就很容易写出一个关于ContentProvider的代码模型了。如下:
package com.example.contentproviderdemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
public class MyProvider extends ContentProvider {
/**
* 匹配table1表的所有行数据
*/
private static final int TABLE1_DIR = 0;
/**
* 匹配table1表的某一个id的行数据
*/
private static final int TABLE1_ITEM = 1;
private static UriMatcher mMatcher;
static{
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加匹配规则
mMatcher.addURI("android.content.ContentProvider.provider", "table1", TABLE1_DIR);
mMatcher.addURI("android.content.ContentProvider.provider", "table1/#", TABLE1_ITEM);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (mMatcher.match(uri)) {
case TABLE1_DIR:
// 查询所有的table1的数据
break;
case TABLE1_ITEM:
// 查询某一个id的数据
break;
default:
break;
}
return null;
}
@Override
public String getType(Uri uri) {
if (mMatcher.match(uri) == TABLE1_DIR){
return "vnd.android.cursor.dir/com.example.contentproviderdemo.provider.table1";
}else if (mMatcher.match(uri) == TABLE1_ITEM){
return "vnd.android.cursor.item/com.example.contentproviderdemo.provider.table1";
}else{
return null;
}
}
}
实例中仅仅是写出query方法,其实update、insert、delete都是差不多的。
介绍了这么多的基础知识,我们有必要实战一下,让自己的印象更加的深刻。具体工程包含两个,一个时contentprovider实现,另一个是测试代码。
1. 先看看PersonProvider代码:
package com.example.contentproviderdemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
public class PersonProvider extends ContentProvider {
private DBHelper mHelper;
private static final int PERSON_DIR = 0;
private static final int PERSON_ITEM = 1;
private static final String tag = "PersonProvider";
private static UriMatcher mMatcher;
static{
// 添加匹配规则
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person", PERSON_DIR);
mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person/#", PERSON_ITEM);
}
@Override
public boolean onCreate() {
mHelper = new DBHelper(getContext(), "test.db");
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
SQLiteDatabase db = mHelper.getReadableDatabase();
switch (mMatcher.match(uri)) {
case PERSON_DIR:
Log.d(tag, "PERSON_DIR");
cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
break;
case PERSON_ITEM:
Log.d(tag, "PERSON_ITEM");
// 获取id.内部是把uri是"/"进行分割了,所以获取get(1)就是id值。
String personID = uri.getPathSegments().get(1);
cursor = db.query("person", projection, "_id = ?", new String[]{personID}, null, null, sortOrder);
break;
default:
Log.d(tag, "Invalid uri");
break;
}
return cursor;
}
@Override
public String getType(Uri uri) {
String result = null;
switch (mMatcher.match(uri)) {
case PERSON_DIR:
result = "vnd.android.cursor.dir/com.example.contentproviderdemo.PersonProvider.provider.person";
break;
case PERSON_ITEM:
result = "vnd.android.cursor.item/com.example.contentproviderdemo.PersonProvider.provider.person";
break;
default:
break;
}
return result;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Uri result = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
switch (mMatcher.match(uri)) {
case PERSON_DIR:
long id = db.insert("person", null, values);
String uriString = "content://com.example.contentproviderdemo.PersonProvider.provider/person/" + id;
result = Uri.parse(uriString);
break;
default:
break;
}
return result;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mHelper.getWritableDatabase();
int result = 0;
switch (mMatcher.match(uri)) {
case PERSON_DIR:
result = db.delete("person", selection, selectionArgs);
break;
case PERSON_ITEM:
String personID = uri.getPathSegments().get(1);
result = db.delete("person", "_id = ?", new String[]{personID});
break;
default:
break;
}
return result;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = mHelper.getWritableDatabase();
int result = 0;
switch (mMatcher.match(uri)) {
case PERSON_DIR:
result = db.update("person", values, selection, selectionArgs);
break;
case PERSON_ITEM:
String personID = uri.getPathSegments().get(1);
result = db.update("person", values, "_id = ?", new String[]{personID});
break;
default:
break;
}
return result;
}
}
代码比较简单,关键地方还有注释,就不介绍了。要想让其它的进程能够访问,可以在AndroidManifest.xml中进行配置。
<provider
android:name="com.example.contentproviderdemo.PersonProvider"
android:authorities="com.example.contentproviderdemo.PersonProvider.provider"
android:exported="true"
></provider>
节点exported的含义是,如果要用于其它进程中组件进行交互就得设置为TRUE,否则会报权限不允许的异常。
经过以上的讲解,我觉得是把contentprivider讲通了。
不知不觉写博客已经坚持一段时间了。以后还得坚持下去,通过博客见证自己的成长。
今天的代码的下载地址为:下载源码