Android中的Content Provider可以实现在权限许可的情况下,在多个应用程序之间共享数据。Android还提供了一些主要数据类型如音频、视频、图片和私人通讯录等现成的Content provider类。当然,如果我们想让我们应用程序的数据公有化,使其它应用也可以访问,那么,我们可以实现一个自己的ContentProvider类。
一种类型的ContentProvider只允许有一个实例,但它可以和多个不同的应用程序或进程的ContentResolver进行通信。进程间的交互就是由ContentResolver和ContentProvider一起完成的。我们可以通过getContentResolver()方法来获得 ContentResolver对象。
数据模型:
ContentProvider以数据表的形式对外暴露,其中每一行数据都有一个唯一的_ID值。每次查询数据时都会返回一个Cursor 对象。 不过使用Cursor对象获取数据前一定要先知道数据的类型。
Android中通过URI来定位各种资源,每一个可以调用的资源都有一个唯一的URI,如图片,视频等。同样,ContentProvider中的每一个表也各有一个唯一的URI。ContentProvider提供的URI都是以content://开头的。在所有ContentProvider交互中都要用到URI,而且,每个ContentResolver方法的第一个参数就是URI,它告诉ContentResolver方法要访问的ContentProvider对象,以及操作的目标表。 因此,为了方便,给自定义ContentProvider中的URI指定一个专门的CONTENT_URI常量是很有必要的。
通过Content Provider查询数据前要准备的:
1,要查询数据的Provider的URI;
2,要查询的数据的列名以及它们的数据类型;其中_ID和_COUNT列唯一某行数据以及总数据行数。
3,如果是某一行数据,还需要知道它的ID值;如果_ID=23 则为“content://. . . ./23”
查询数据:
可以用ContentResolver.query()方法或Activity.managedQuery()方法来获取结果Cursor。后者得到的Cursor的生命周期由Activity来管理,而不需要我们再手动关闭游标,我们还可以方便的精确控制它的加载与释放。Activity.startManagingCursor()方法可以将未受管理的游标加入到管理列表中(注:2.3版以后推荐使用getLoaderManager()得到LoaderManger对象来管理游标)stopManagingCursor(Cursor)可以停止自动管理功能。
1,获取URI:
ContentUris.withAppendedId()
Uri.withAppendedPath()
以上两个方法都可以实现将参数(ID)添加到查询URI当中去。
而ContentUris.parseId(uri)方法可以获取URI中的ID部分。
示例:
import android.provider.Contacts.People;
import android.content.ContentUris;
import android.net.Uri;
import android.database.Cursor;
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");
Cursor cur = managedQuery(myPerson, null, null, null, null);
2,读取数据:
private void getColumnData(Cursor cur){
if (cur.moveToFirst()) {
String name;
String phoneNumber;
int nameColumn = cur.getColumnIndex(People.NAME);
int phoneColumn = cur.getColumnIndex(People.NUMBER);
String imagePath;
do {
// Get the field values
name = cur.getString(nameColumn);
phoneNumber = cur.getString(phoneColumn);
// Do something with the values.
...
} while (cur.moveToNext());
}
}
如果是较小的二进制数据,可以直接像普通数据一样保存到表中去,但如果二进制数据量较大,且表的URI是content:URI形式的,则我们需要采用ContentResolver.openInputStream()方法来获得数据流,以便读取数据。
所有数据的更改都需要调用ContentResolver的方法来完成。
首先,将要更改的数据保存到ContentValues对象中,然后调用ContentResolver的相应方法面完成更新操作,示例:
增加新记录:
ContentValues values = new ContentValues();
values.put(People.NAME, "Abraham Lincoln");
values.put(People.STARRED, 1);
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
在原有记录上增加新值:
Uri phoneUri = null;
phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.Phones.NUMBER, "1233214567");
getContentResolver().insert(phoneUri, values);
其中的CONTENT_DIRECTORY是一个常量,表明这是追加的数据。
如果数据是content:URI形式的二进制大对象,调用ContentResolver.openOutputStream()来保存。
大二进制数据(多媒体数据)处理示例:
import android.provider.MediaStore.Images.Media;
import android.content.ContentValues;
import java.io.OutputStream;
// Save the name and description of an image in a ContentValues map.
ContentValues values = new ContentValues(3);
values.put(Media.DISPLAY_NAME, "road_trip_1");
values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");
values.put(Media.MIME_TYPE, "image/jpeg");
// Add a new record without the bitmap, but with the values just set.
// insert() returns the URI of the new record.
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
// Now get a handle to the file for that record, and save the data into it.
// Here, sourceBitmap is a Bitmap object representing the file to save to the database.
try {
OutputStream outStream = getContentResolver().openOutputStream(uri);
sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
outStream.close();
} catch (Exception e) {
Log.e(TAG, "exception while writing image", e);
}
实现我们自己的Content Provider:
1,需要创建一个类并且继承ContentProvider类,并实现这个抽象类中的六个方法。
public int delete(Uri uri, String selection, String[] selectionArgs){}
public Uri insert(Uri uri, ContentValues values) {}
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {} //SQLiteCursor,MatrixCursor
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {}
public boolean onCreate() {}
public String getType(Uri uri) {}
2,定义URI常量:
public static final Uri CONTENT_URI;
CONTENT_URI=Uri.parse("content://com.example.codelab.transportationprovider");
3,定义列名,不要忘记_ID列。
public static String ColumnName
4,如果我们的数据在是新的类型,必须在getType ()中返回这个定义好的MIME 类型,自定义类型的名称格式为:
vnd.android.cursor.item/vnd.yourcompanyname.contenttype
vnd.android.cursor.dir/vnd.yourcompanyname.contenttype
5,当保存大二进制数据时,列名下实际存储的是content:URI,另声明一个“_data”列,保存文件的真实路径,以供ContentResolver.openInputStream()时访问。
6,当数据发生改变时,最好还要调用ContentResolver.notifyChange()方法,来通知调用程序。
7,在AndroidManifest.xml中声明ContentProvider。
示例:
<provider android:name=".AutoInfoProvider"
android:authorities="com.example.autos.autoinfoprovider"
. . . />
</provider>
<provider>标签的属性中还可以设置ContentProvider的更多参数。
一个完整的自定义ContentProvider及共享调用与事件侦听的例子:
数据结构User.java:
Java代码
1. package com.yaku.pojo;
2.
3. public class User {
4. private int id;
5. private String name;
6. private int age;
7.
8. public User(int id, String name, int age) {
9. super();
10. this.id = id;
11. this.name = name;
12. this.age = age;
13. }
14. public int getId() {
15. return id;
16. }
17. public void setId(int id) {
18. this.id = id;
19. }
20. public String getName() {
21. return name;
22. }
23. public void setName(String name) {
24. this.name = name;
25. }
26. public int getAge() {
27. return age;
28. }
29. public void setAge(int age) {
30. this.age = age;
31. }
32. @Override
33. public String toString() {
34. return "User [age=" + age + ", id=" + id + ", name=" + name + "]";
35. }
36. }
数据库操作DBOpenHelper.java:
Java代码
1. package com.yaku.db;
2.
3. import android.content.Context;
4. import android.database.sqlite.SQLiteDatabase;
5. import android.database.sqlite.SQLiteOpenHelper;
6.
7. public class DBOpenHelper extends SQLiteOpenHelper {
8. private static final String DBNAME = "yaku.db"; //数据库名称
9. private static final int DBVER = 1;//数据库版本
10.
11. public DBOpenHelper(Context context) {
12. super(context, DBNAME, null, DBVER);
13. }
14.
15. @Override
16. public void onCreate(SQLiteDatabase db) {
17. String sql = "CREATE TABLE user (userid integer primary key autoincrement, name varchar(20), age integer)";
18. db.execSQL(sql);//执行有更改的sql语句
19. }
20.
21. @Override
22. public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
23. db.execSQL("DROP TABLE IF EXISTS user");
24. onCreate(db);
25. }
26.
27. }
对外共享处理类ContentProviderUser.java:
Java代码
1. package com.yaku.ContentProvider;
2.
3. import com.yaku.db.DBOpenHelper;
4.
5. import android.content.ContentProvider;
6. import android.content.ContentUris;
7. import android.content.ContentValues;
8. import android.content.UriMatcher;
9. import android.database.Cursor;
10. import android.database.sqlite.SQLiteDatabase;
11. import android.net.Uri;
12.
13. public class ContentProviderUser extends ContentProvider {
14. private DBOpenHelper dbOpenHelper;
15. //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
16. private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
17. private static final int USERS = 1;
18. private static final int USER = 2;
19. static{
20. //如果match()方法匹配content://com.yaku.ContentProvider.userprovider/user路径,返回匹配码为1
21. MATCHER.addURI("com.yaku.ContentProvider.userprovider", "user", USERS);
22. //如果match()方法匹配content://com.yaku.ContentProvider.userprovider/user/123路径,返回匹配码为2
23. MATCHER.addURI("com.yaku.ContentProvider.userprovider", "user/#", USER);//#号为通配符
24. }
25. @Override
26. public int delete(Uri uri, String selection, String[] selectionArgs) {
27. SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
28. int count = 0;
29. switch (MATCHER.match(uri)) {
30. case USERS:
31. count = db.delete("user", selection, selectionArgs);
32. return count;
33. case USER:
34. //ContentUris类用于获取Uri路径后面的ID部分
35. long id = ContentUris.parseId(uri);
36. String where = "userid = "+ id;
37. if(selection!=null && !"".equals(selection)){
38. where = selection + " and " + where;
39. }
40. count = db.delete("user", where, selectionArgs);
41. return count;
42. default:
43. throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
44. }
45. }
46.
47. /**
48. * 该方法用于返回当前Url所代表数据的MIME类型。
49. * 如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头
50. * 如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头
51. */
52. @Override
53. public String getType(Uri uri) {
54. switch (MATCHER.match(uri)) {
55. case USERS:
56. return "vnd.android.cursor.dir/user";
57.
58. case USER:
59. return "vnd.android.cursor.item/user";
60.
61. default:
62. throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
63. }
64. }
65.
66. @Override
67. public Uri insert(Uri uri, ContentValues values) {
68. SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
69. switch (MATCHER.match(uri)) {
70. case USERS:
71. long rowid = db.insert("user", "name", values);
72. Uri insertUri = ContentUris.withAppendedId(uri, rowid);//得到代表新增记录的Uri
73. this.getContext().getContentResolver().notifyChange(uri, null);
74. return insertUri;
75.
76. default:
77. throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
78. }
79. }
80.
81. @Override
82. public boolean onCreate() {
83. this.dbOpenHelper = new DBOpenHelper(this.getContext());
84. return false;
85. }
86.
87. @Override
88. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
89. String sortOrder) {
90. SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
91. switch (MATCHER.match(uri)) {
92. case USERS:
93. return db.query("user", projection, selection, selectionArgs, null, null, sortOrder);
94. case USER:
95. long id = ContentUris.parseId(uri);
96. String where = "userid = "+ id;
97. if(selection!=null && !"".equals(selection)){
98. where = selection + " and " + where;
99. }
100. return db.query("user", projection, where, selectionArgs, null, null, sortOrder);
101. default:
102. throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
103. }
104. }
105.
106. @Override
107. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
108. SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
109. int count = 0;
110. switch (MATCHER.match(uri)) {
111. case USERS:
112. count = db.update("person", values, selection, selectionArgs);
113. return count;
114. case USER:
115. long id = ContentUris.parseId(uri);
116. String where = "userid = "+ id;
117. if(selection!=null && !"".equals(selection)){
118. where = selection + " and " + where;
119. }
120. count = db.update("user", values, where, selectionArgs);
121. return count;
122. default:
123. throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
124. }
125. }
126. }
单元测试类(在另一个应用中):
Java代码
1. package com.yaku.ContentProvider;
2.
3. import android.content.ContentResolver;
4. import android.content.ContentValues;
5. import android.database.Cursor;
6. import android.net.Uri;
7. import android.test.AndroidTestCase;
8. import android.util.Log;
9.
10. /**
11. * 对ContentProvider工程中的ContentProviderActivity进行单元测试
12. */
13. public class ContentProviderActivityTest extends AndroidTestCase {
14. private static final String TAG = "ContentProvider";
15. //往内容提供者添加数据
16. public void testInsert() throws Throwable{
17. ContentResolver contentResolver = this.getContext().getContentResolver();
18. Uri insertUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");
19. ContentValues values = new ContentValues();
20. values.put("name", "道长");
21. values.put("age", 86);
22. Uri uri = contentResolver.insert(insertUri, values);
23. Log.i(TAG, uri.toString());
24. }
25.
26. //更新内容提供者中的数据
27. public void testUpdate() throws Throwable{
28. ContentResolver contentResolver = this.getContext().getContentResolver();
29. Uri updateUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user/1");
30. ContentValues values = new ContentValues();
31. values.put("name", "青眉道长");
32. contentResolver.update(updateUri, values, null, null);
33. }
34.
35. //从内容提供者中删除数据
36. public void testDelete() throws Throwable{
37. ContentResolver contentResolver = this.getContext().getContentResolver();
38. Uri deleteUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user/1");
39. contentResolver.delete(deleteUri, null, null);
40. }
41.
42. //获取内容提供者中的数据
43. public void testFind() throws Throwable{
44. ContentResolver contentResolver = this.getContext().getContentResolver();
45. Uri selectUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");
46. Cursor cursor = contentResolver.query(selectUri, null, null, null, "userid desc");
47. while(cursor.moveToNext()){
48. int id = cursor.getInt(cursor.getColumnIndex("userid"));
49. String name = cursor.getString(cursor.getColumnIndex("name"));
50. int age = cursor.getInt(cursor.getColumnIndex("age"));
51. Log.i(TAG, "id="+ id + ",name="+ name+ ",age="+ age);
52. }
53. }
54.
55. }
监听数据的变化:
Java代码
1. <span style="font-size: medium;">package com.yaku.ContentProvider;
2.
3. import android.content.ContentResolver;
4. import android.content.ContentValues;
5. import android.database.Cursor;
6. import android.net.Uri;
7. import android.test.AndroidTestCase;
8. import android.util.Log;
9.
10. /**
11. * 监听数据变化
12. */
13. public class OtherContentProviderTest extends AndroidTestCase {
14. private static final String TAG = "OtherContentProvider";
15.
16. @Override
17. public void onCreate(Bundle savedInstanceState) {
18. super.onCreate(savedInstanceState);
19. setContentView(R.layout.main);
20.
21. Uri insertUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");
22. ContentResolver contentResolver = this.getContentResolver();
23. //对指定uri进行监听,如果该uri代表的数据发生变化,就会调用PersonObserver中的onChange()
24. contentResolver.registerContentObserver(insertUri, true, new PersonObserver(new Handler()));
25. }
26.
27. private final class PersonObserver extends ContentObserver{
28. public PersonObserver(Handler handler) {
29. super(handler);
30. }
31.
32. @Override
33. public void onChange(boolean selfChange) {
34. ContentResolver contentResolver = getContentResolver();
35. Uri selectUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");
36. Cursor cursor = contentResolver.query(selectUri, null, null, null, "userid desc");
37. while(cursor.moveToNext()){
38. int id = cursor.getInt(cursor.getColumnIndex("userid"));
39. String name = cursor.getString(cursor.getColumnIndex("name"));
40. int age = cursor.getInt(cursor.getColumnIndex("age"));
41. Log.i(TAG, "id="+ id + ",name="+ name+ ",age="+ age);
42. }
43. }
44. }
45. }
《完》
※※※※※※※※※※※※※※--我的程序人生--※※※※※※※※※※※※※※