ContentProvider-----转 http://cache.baiducontent.com/c?m=9d78d513d99056ae28fa950d1a17a771695797144fc0a61068a4e25f92144c30427193b930541513a3b66b6770b83f5dfd813765460137c79ddf893acacae23f5f8f3045710bf24405d269b8bd4632b150875b99b869edad843684afa2828208159d0f436d8081c91a5d509139a75426e2d68e38614866b0fa4661e828733e9e5357c040ee9732797183e1ae575bc25ac7106280db43a74e65a265d2081b5553d13ba67e263140e55854f5533c74859b57f05b7f5753b55fc4bfd6b79b5ffcdbfd409eee8cef719a3aa083a8ee70316d4faa7be3feaab24a674103cecaa947996da1b1b7e517bc478a075ab85e762b769e35c9d79216b4311cafb675fa2c21212c08d8b174fd76736f31a8391fb01cc472ba8d6b&p=93769a478d9b17b508e2977f0c4091&newp=9a769a479cd806f642bd9b7d0c17cd231610db2151d3d01035cfd41189&user=baidu&fm=sc&query=A%3A%B1%EA%D7%BC%C7%B0%D7%BA%2C%D3%C3%C0%B4%CB%B5%C3%F7%D2%BB%B8%F6Content+Provider%BF%D8%D6%C6%D5%E2%D0%A9%CA%FD%BE%DD%2C%CE%DE%B7%A8%B8%C4%B1%E4%B5%C4%3B&qid=&p1=5
一个应用程序可以创建自己的数据,这个数据对该应用程序来说是私有的,外界更本看不到,也不知道数据是如何 存储的,或者是使用数据库还是使用文件,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和这个程序里的数据打交道,例 如:添加(insert)、删除(delete)、查询(query)、修改(update)。
Android 为我们提供了ContentProvider来实现数据的共享,一个程序如果想让别的程序可以操作自己的数据,就定义自己的 ContentProvider,然后在AndroidManifest.xml中注册,其他application可以通过获取 ContentResolver通过Uri来操作上一程序的数据。
Android中的电话本等数据就是通过ContentProvider实现数据共享的,系统中有很多已经存在的共享Uri。我们可以使用ContentResolver通过Uri来操作不同表的数据;如Contacts.People.CONTENT_URI
什么是URI?
将其分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"
B:URI的标识,它定义了是哪个Content Provider提 供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称 ;"content://hx.android.text.myprovider"
C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://hx.android.text.myprovider/tablename"
D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id
这里主要是说明如何创建自己的ContentProvider,这里有几个常用的类
UriMatcher:用于匹配Uri,它的用法如下:
1. 首先把你需要匹配Uri路径全部给注册上,如下: //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。 UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1 uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码 //如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2 uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
2. 注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用 addURI()方法传入的第三个参数,假设匹配 content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回的匹配码为1。
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
· withAppendedId(uri, id)用于为路径加上ID部分
· parseId(uri)方法用于从路径中获取ID部分
其他要用到的类程序中已添加说明自己写了一个小的测试程序,附在下面有想了解的可以看一下程序是基于同一个application的。
对应自定义ContentProvider的使用找到以下资料:
Android开发中的ContentProvider数据模型概述
Android中ContentProvider和ContentResolver使用入门
ContentProvider 是什么时候创建的,是谁创建的?访问某个应用程序共享的数据,是否需要启动这个应用程序?这个问题在 Android SDK中没有明确说明,但是从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。 这就要求在AndroidManifest.XML中使用元素明确定义。 可能会有多个程序同时通过ContentResolver访问一个ContentProvider,会不会导致像数据库那样的“脏数 据”?这个问题一方面需要数据库访问的同步, 尤其是数据写入的同步,在AndroidManifest.XML中定义ContentProvider的时 候,需要考虑是元素multiprocess属性的值;另外一方面Android在ContentResolver中提供了notifyChange() 接口,在数据改变时会通知其他ContentObserver,这个地方应该使用了观察者模式,在ContentResolver中应该有一些类似 register,unregister的接口。 至此,已经对ContentProvider提供了比较全面的分析,至于如何创建ContentProvider,可通过2种方法:创建一个属于你自己的 ContentProvider或者将你的数据添加到一个已经存在的ContentProvider中,当然前提是有相同数据类型并且有写入 Content provider的权限。
http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380147d8c8c4668d4e419ce3b4c413037bfa6663f405a8e906b6075a84d56e0f63574670123b59982db1f9fac925f75ce786a6459db0144dc4ab8ca3632c053875b99b869e4ad874184d8d7c4af2544cb23120b85e7fb581762cf78806726e3d78e3e144861bbfa4167e829713e9e5357c737ee9035797680e1a9595bc25ac7171180df45a73f62a263ae086f2053a13ce0550d6067bc0930ff357f44cfaf06b17d395722c65fc3cdd6c0e15ffad8fd409cedcadf38e367f1b2eaee07406721f132bfdbaac34e733174cebaa901b244abfa9dad3d9e03a1674bca58615c61d96ae495ff5de5135ce3976be95e2431297ac8c373e80476&p=91759a46d7c003fc57efdc37460796&newp=926fcf16d9c116ff57ee957d5c6498231610db2151d7db1628&user=baidu&fm=sc&query=A%3A%B1%EA%D7%BC%C7%B0%D7%BA%2C%D3%C3%C0%B4%CB%B5%C3%F7%D2%BB%B8%F6Content+Provider%BF%D8%D6%C6%D5%E2%D0%A9%CA%FD%BE%DD%2C%CE%DE%B7%A8%B8%C4%B1%E4%B5%C4%3B&qid=&p1=2
一、Content Provider基本概念
1、ContentProvider为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。使用表的形式来组织数据。
2、使用ContentProvider可以在不同的应用程序之间共享数据。
3、Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。
ContentProvider所提供的函数:
query(),insert(),update(),delete(),getType(),onCreate()等。
二、URI(统一资源标识符)的使用方法
为系统的每一个资源给其一个名字,比方说通话记录。
1、每一个ContentProvider都拥有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。
2、Android所提供的ContentProvider都存放在android.provider包中。 将其分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"
B:URI 的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称;"content://hx.android.text.myprovider"
C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://hx.android.text.myprovider/tablename"
D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id
三、ContentProvider的实现过程
自己实现ContentProvider不常见,因为可能不需要和别的应用程序交换数据。使用内置的ContentProvider比较多。
1、定义一个CONTENT_URI常量,提供了访问ContentProvider的标识符。
publicstaticfinalUri CONTENT_URI =Uri.parse("content://com.example.codelab.transportationprovider");
其中:content是协议
Com.exmaple.codelab.transportationprovider是类名,包含完整的包名。
Uri.parse将一个字符串转换成Uri类型。
如果Provider包含子表,同样定义包含字表的CONTENT_URI。
content://com.example.codelab.transportationprovider/train
content://com.example.codelab.transportationprovider/air/domestic
content://com.example.codelab.transportationprovider/air/international
然后定义列,确保里面包含一个_id的列。
2、定义一个类,继承ContentProvider。
public class FirstContentProvider extends ContentProvider
先介绍一下ContentProvider用到的UriMatcher。UriMatcher的一个重要的函数是match(Uri uri)。这个函数可以匹配Uri,根据传入的不同Uri返回不同的自定义整形值,以表明Uri访问的不同资源的类型。
例如:
public static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(Book.AUTHORITY, "item", Book.ITEM);
uriMatcher.addURI(Book.AUTHORITY, "item/#", Book.ITEM_ID);
}
这里UriMatcher类型的静态字段是用来匹配传入到ContentProvider中的Uri的类。其构造方法传入的匹配码是使用match()方法匹配根路径时返回的值,这个匹配码可以为一个大于零的数表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径。 addURI()方法是用来增加其他URI匹配路径的,第一个参数传入标识ContentProvider的AUTHORITY字符串。第二个参数传入需要匹配的路径,这里的#号为通配符,代表匹配任意数字,另外还可以用*来匹配任意文本。第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码。 例如:sMatcher.addURI(“com.test.provider.personprovider”, “person”, 1);如果match()方法匹配content://com.test.provider.personprovider/person路径,返回匹配码为1。
3、实现query,insert,update,delete,getType和onCreate方法。
4、在AndroidManifest.xml当中进行声明。
<!-- android:name是完成ContentProvider类的全称
android:authorities是和FirstProvidermetaData中的常量AUTHORITY的值一样,否则会报错
-->
<provider android:name="com.bj.FirstContentProvider"
android:authorities="com.bj.firstcontentprovider"
/>
四、具体代码
Activity19Activity.java
public class Activity19Activity extends Activity {
private Button queryButton = null;
private Button insertButton = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
queryButton = (Button) this.findViewById(R.id.query);
queryButton.setOnClickListener(newQueryListener());
insertButton = (Button) this.findViewById(R.id.insert);
insertButton.setOnClickListener(newInsertListener());
System.out.println(getContentResolver().getType(FirstProvidermetaData.UserTableMetaData.CONTENT_URI));
}
class InsertListener implementsOnClickListener {
@Override
public void onClick(View v) {
// TODOAuto-generated method stub
ContentValues values = new ContentValues();
values.put(FirstProvidermetaData.UserTableMetaData.USER_NAME,
"michal");
Uri uri = getContentResolver()
.insert(
FirstProvidermetaData.UserTableMetaData.CONTENT_URI,
values);
System.out.println("uri--->" + uri.toString());
}
}
class QueryListener implementsOnClickListener {
public void onClick(View v) {
Cursor c = getContentResolver().query(
FirstProvidermetaData.UserTableMetaData.CONTENT_URI, null,
null, null, null);
while (c.moveToNext()) {
System.out.println(c.getString(c.getColumnIndex("username")));
}
}
}
}
FirstContentProvider.java
public class FirstContentProvider extendsContentProvider {
// 当别的程序来访问这个ContentProvider,是通过Uri来访问的,UriMatcher检查是否符合标准
// 给uri起一个规则,返回数字
public static final UriMatcher uriMatcher;
// 下面定义两个规则
public static final int INCOMING_USER_COLLECTION = 1;
public static final int INCOMING_USER_SINGLE = 2;
private DatabaseHelper dh;
static {//下面的users前面不能加/
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(FirstProvidermetaData.AUTHORITY, "users",
INCOMING_USER_COLLECTION);
uriMatcher.addURI(FirstProvidermetaData.AUTHORITY, "users/#",
INCOMING_USER_SINGLE);
}
//有点类似于sql里面表的别名,这个也是给列其别名,必须要用
//列的别名还是原来的名,没必要修改
public static HashMap<String,String> userProjectionMap;
static{
userProjectionMap = newHashMap<String,String>();
userProjectionMap.put(UserTableMetaData._ID, UserTableMetaData._ID );
userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODOAuto-generated method stub
System.out.println("delete");
return 0;
}
// 根据传入的URI,返回该URI所表示的数据类型
// 也就是说,我们通过URI要访问的数据,返回什么类型
@Override
public String getType(Uri uri) {
// TODOAuto-generated method stub
System.out.println("getType");
switch (uriMatcher.match(uri)) {
case INCOMING_USER_COLLECTION:
// UserTableMetaData是FirstProvidermetaData的内部类
return UserTableMetaData.CONTENT_TYPE;
case INCOMING_USER_SINGLE:
return UserTableMetaData.CONTENT_TYPE_ITEM;
default:
throw new IllegalArgumentException("Unknown uri" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODOAuto-generated method stub
System.out.println("insert");
SQLiteDatabase db = dh.getWritableDatabase();
//返回表中自动增长的列的值,否则返回-1
long rowId = db.insert(UserTableMetaData.TABLE_NAME,null, values);
if(rowId>0){
//将rowId追加到后面
//contentUris:用来处理Uri的工具类
Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);
//通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(insertedUserUri, null);
return insertedUserUri;
}
throw new SQLException("Failed to insert row into "+uri);
}
//是一个回调方法,所以说在ContentProvider创建的时候执行
//也就是创建这个DatabaseHelper对象
@Override
public boolean onCreate() {
// TODOAuto-generated method stub
//getContext得到当前正在运行着的context
dh = new DatabaseHelper(getContext(),FirstProvidermetaData.DATABASE_NAME);
System.out.println("on create");
SQLiteDatabase db = dh.getReadableDatabase();
return true;
}
//projection:查询的列有哪些
//selection:where子句的内容,可以用?
//selectionArgs:占位符对应的参数
//sortOrder:排序
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODOAuto-generated method stub
System.out.println("query");
//创建一个查询的语句
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch(uriMatcher.match(uri)){
case INCOMING_USER_COLLECTION:
//设置查询哪张表
qb.setTables(UserTableMetaData.TABLE_NAME);
qb.setProjectionMap(userProjectionMap);
break;
case INCOMING_USER_SINGLE:
qb.setTables(UserTableMetaData.TABLE_NAME);
qb.setProjectionMap(userProjectionMap);
//添加where条件,getPathSegments:得到uri的path部分content:XXX/user/1,get(1)得到1
qb.appendWhere(UserTableMetaData._ID+"="+uri.getPathSegments().get(1));
break;
}
String orderBy;
if(TextUtils.isEmpty(sortOrder)){
orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;
}
else
{
orderBy = sortOrder;
}
SQLiteDatabase db = dh.getWritableDatabase();
//下面的query使用qb这个对象
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
//也是通知下
c.setNotificationUri(getContext().getContentResolver(), uri);
System.out.println("query");
return c;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODOAuto-generated method stub
return 0;
}
}
FirstProvidermetaData.java
public class FirstProvidermetaData {
public static final String AUTHORITY="com.bj.firstcontentprovider"; //继承了contentprovider的类的全名
//数据库名称
public static final String DATABASE_NAME = "FirstProvider.db";
//数据库的版本
public static final int DATABASE_VERSION = 1;
//表名
public static final String USERS_TABLE_NAME = "users";
public static final class UserTableMetaData implements BaseColumns{
//表名
public static final String TABLE_NAME="users";
//访问该ContentProvider的URI
public static final Uri CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/users");
public static final String CONTENT_TYPE="vnd.android.cursor.dir/vnd.firstprovider.user";
public static final String CONTENT_TYPE_ITEM="vnd.android.cursor.item/vnd.firstprovider.user";
//列名,在users表中添加一个名为name的列
public static final String USER_NAME="name";
//默认排序方式
public static final String DEFAULT_SORT_ORDER="_id desc";
}
}