Android中的ContentProvider是一种多应用数据共享的机制,任何时候同一Provider只会创建一次,是由系统进行初始化和管理的。本文中将通过实现一个简单通讯录的插入、删除、查询操作来让你了解ContentProvider机制极其自定义过程。读过本系列(读取手机中通讯录)和(sqlite数据库操作)的读者可以发现,本文中实现的应用以这两篇内容为基础的。虽然,本文的MyContentProvider使用sqlite数据库进行持久化存储操作,包装后以ContentProvider机制供各app调用。但是,为理解ContentProvider,你必须有这样两个概念:一、它是一种可以跨应用共享数据的机制,正如本文开头所说。二、底层的存储在实现你的ContentProvider的时候可以自定义,即可以为数据库、也可以为文件,持久化或非持久化存储的其他形式。 程序截图如下:
类AcMain.java,为主Activity,用来实现应用的主界面控件和流程,代码:
1. //AcMain.java
2. package jtapp.contentproviders;
3. 4. import android.app.Activity;
5. import android.content.ContentValues;
6. import android.database.Cursor;
7. import android.os.Bundle;
8. import android.view.View;
9. import android.view.View.OnClickListener;
10. import android.widget.Button;
11. import android.widget.TextView;
12. 13. public class AcMain extends Activity {
14. private Button btInsertData = null;
15. private Button btViewData = null;
16. private Button btDelOne = null;
17. private Button btClearAll = null;
18. 19. /** Called when the activity is first created. */
20. @Override
21. public void onCreate(Bundle savedInstanceState) {
22. super.onCreate(savedInstanceState);
23. setContentView(R.layout.main);
24. 25. btInsertData = (Button) findViewById(R.id.Button02);
26. btInsertData.setOnClickListener(new ClickViewHandler());
27. btViewData = (Button) findViewById(R.id.Button03);
28. btViewData.setOnClickListener(new ClickViewHandler());
29. btDelOne = (Button) findViewById(R.id.Button04);
30. btDelOne.setOnClickListener(new ClickViewHandler());
31. btClearAll = (Button) findViewById(R.id.Button05);
32. btClearAll.setOnClickListener(new ClickViewHandler());
33. }
34. 35. public class ClickViewHandler implements OnClickListener {
36. @Override
37. public void onClick(View v) {
38. if (v == btInsertData) {
39. InsertSomeRecords();
40. } else if (v == btViewData) {
41. ViewRecords();
42. } else if (v == btDelOne) {
43. DelOne();
44. } else if (v == btClearAll) {
45. DelAllPeople();
46. }
47. }
48. 49. private void DelAllPeople() {
50. getContentResolver().delete(MyContacts.CONTENT_URI, null, null);
51. }
52. 53. private void DelOne() {
54. int id;
55. Cursor c = getContentResolver().query(
56. MyContacts.CONTENT_URI, null,
57. null, null, MyContacts.NAME + " ASC");
58. if (c.moveToFirst()) {
59. int idxID = c.getColumnIndex(MyContacts._ID);
60. id = c.getInt(idxID);
61. getContentResolver().delete(MyContacts.CONTENT_URI,
62. MyContacts._ID + " = " + id, null);
63. }
64. }
65. 66. private void ViewRecords() {
67. // Make the query
68. Cursor c = managedQuery(MyContacts.CONTENT_URI, null, null, null,
69. MyContacts._ID);
70. StringBuilder sbRecords = new StringBuilder("");
71. if (c.moveToFirst()) {
72. int idxID = c.getColumnIndex(MyContacts._ID);
73. int idxName = c.getColumnIndex(MyContacts.NAME);
74. int idxNumber = c.getColumnIndex(MyContacts.NUMBER1);
75. int idxEmail = c.getColumnIndex(MyContacts.EMAIL);
76. // Iterator the records
77. do {
78. sbRecords.append(c.getInt(idxID));
79. sbRecords.append(". ");
80. sbRecords.append(c.getString(idxName));
81. sbRecords.append(", ");
82. sbRecords.append(c.getString(idxNumber));
83. sbRecords.append(", ");
84. sbRecords.append(c.getString(idxEmail));
85. sbRecords.append("/n");
86. } while (c.moveToNext());
87. }
88. c.close();
89. // Refresh the content of TextView
90. ((TextView)(findViewById(
91. R.id.TextView01))).setText(sbRecords);
92. }
93. 94. private void InsertSomeRecords() {
95. ContentValues values = new ContentValues();
96. values.put(MyContacts.NAME, "朱元璋");
97. values.put(MyContacts.NUMBER1, "13965625585");
98. getContentResolver().insert(MyContacts.CONTENT_URI, values);
99. values.clear();
100. values.put(MyContacts.NAME, "玄烨");
101. values.put(MyContacts.EMAIL, "xueye1772@gmail.com");
102. getContentResolver().insert(MyContacts.CONTENT_URI, values);
103. }
104. }
105. }
复制代码
代码中通过getContentResolver()调用contentprovider的delete、insert等操作,由系统根据uri决定调用哪个provider。
query查询除了用getContentResolver().query以外,还可以用activity.managedQuery调用,后者的Cursor的生命周期是由activity来自动管理,本文中的query就是属于这种情况。
主界面ui定义,main.xml,代码:
1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2. android:orientation="vertical" android:layout_width="fill_parent"
3. android:layout_height="fill_parent">
4. <TextView android:layout_width="fill_parent"
5. android:layout_height="wrap_content" android:text="@string/hello" />
6. <TableRow android:id="@+id/TableRow01" android:layout_width="wrap_content"
7. android:layout_height="wrap_content">
8. <Button android:text="insert some records" android:id="@+id/Button02"
9. android:height="30px" android:layout_width="wrap_content"
10. android:layout_height="wrap_content" />
11. </TableRow>
12. <TableRow android:id="@+id/TableRow01" android:layout_width="wrap_content"
13. android:layout_height="wrap_content">
14. <Button android:text="delete one" android:id="@+id/Button04"
15. android:height="30px" android:layout_width="wrap_content"
16. android:layout_height="wrap_content" />
17. <Button android:text="clear all" android:id="@+id/Button05"
18. android:height="30px" android:layout_width="wrap_content"
19. android:layout_height="wrap_content" />
20. </TableRow>
21. <Button android:text="view records" android:id="@+id/Button03"
22. android:height="30px" android:layout_width="wrap_content"
23. android:layout_height="wrap_content" android:layout_gravity="center" />
24. <TextView android:text="..." android:id="@+id/TextView01"
25. android:layout_width="wrap_content" android:layout_height="wrap_content" />
26. </LinearLayout>
复制代码
MyContentProvider.java,代码:
1. package jtapp.contentproviders;
2. import android.content.ContentProvider;
3. import android.content.ContentUris;
4. import android.content.ContentValues;
5. import android.content.Context;
6. import android.database.Cursor;
7. import android.database.SQLException;
8. import android.database.sqlite.SQLiteDatabase;
9. import android.database.sqlite.SQLiteQueryBuilder;
10. import android.database.sqlite.SQLiteStatement;
11. import android.net.Uri;
12. import android.text.TextUtils;
13. import android.util.Log;
14. 15. public class MyContactsProvider extends ContentProvider {
16. private static final String TAG = "MyContactsProvider";
17. private static SQLiteDatabase mDB;
18. 19. private static void createTablesIfNotExists() {
20. mDB.execSQL("CREATE TABLE IF NOT EXISTS "
21. + MyContacts.TB_NAME + " ("
22. + MyContacts._ID + " INTEGER PRIMARY KEY,"
23. + MyContacts.NAME + " VARCHAR,"
24. + MyContacts.NUMBER1 + " VARCHAR,"
25. + MyContacts.EMAIL + " VARCHAR)");
26. }
27. 28. @Override
29. public int delete(Uri uri, String selection, String[] selectionArgs) {
30. int count;
31. switch (MyContacts.uriMatcher.match(uri)) {
32. case MyContacts.CONTACTS:
33. count = mDB.delete(MyContacts.TB_NAME,
34. selection, selectionArgs);
35. break;
36. case MyContacts.CONTACT_ID:
37. String contactID = uri.getPathSegments().get(1);
38. count = mDB.delete(MyContacts.TB_NAME,
39. MyContacts._ID + "=" + contactID, selectionArgs);
40. break;
41. default: throw new IllegalArgumentException(
42. "Unsupported URI: " + uri);
43. }
44. return count;
45. }
46. 47. @Override
48. public String getType(Uri uri) {
49. switch (MyContacts.uriMatcher.match(uri)) {
50. case MyContacts.CONTACTS:
51. return "vnd.android.cursor.dir/vnd.jtapp.contacts";
52. case MyContacts.CONTACT_ID:
53. return "vnd.android.cursor.item/vnd.ambow.contacts";
54. default:
55. throw new IllegalArgumentException("Unsupported URI: " + uri);
56. }
57. }
58. 59. @Override
60. public Uri insert(Uri uri, ContentValues contentValues) {
61. long rowId = mDB.insert(MyContacts.TB_NAME, null, contentValues);
62. if (rowId > 0) {
63. Uri noteUri =
64. ContentUris.withAppendedId(MyContacts.CONTENT_URI,rowId);
65. getContext().getContentResolver().notifyChange(noteUri, null);
66. Log.d(TAG+"insert",noteUri.toString());
67. return noteUri;
68. } else {
69. throw new SQLException("Failed to insert row into " + uri);
70. }
71. }
72. 73. @Override
74. public boolean onCreate() {
75. if (mDB == null) {
76. mDB = this.getContext().openOrCreateDatabase(MyContacts.TB_NAME,
77. Context.MODE_PRIVATE, null);
78. createTablesIfNotExists();
79. }
80. return mDB != null;
81. }
82. 83. @Override
84. public Cursor query(Uri uri, String[] projection, String selection,
85. String[] selectionArgs, String sortOrder) {
86. SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
87. qb.setTables(MyContacts.TB_NAME);
88.
89. switch (MyContacts.uriMatcher.match(uri)) {
90. case MyContacts.CONTACT_ID:
91. qb.appendWhere(
92. MyContacts._ID + "=" + uri.getPathSegments().get(1));
93. break;
94. }
95. String orderBy;
96. if (TextUtils.isEmpty(sortOrder)) {
97. orderBy = MyContacts._ID;
98. } else {
99. orderBy = sortOrder;
100. }
101. Cursor c = qb.query(mDB, projection, selection,
102. selectionArgs,null, null,orderBy);
103. return c;
104. }
105. 106. @Override
107. public int update(Uri uri, ContentValues contentValues, String selection,
108. String[] selectionArgs) {
109. 110. Log.d(TAG+"update",contentValues.toString());
111. Log.d(TAG+"update",uri.toString());
112.
113. int count;
114. switch (MyContacts.uriMatcher.match(uri)) {
115. case MyContacts.CONTACTS:
116. Log.d(TAG+"update",MyContacts.CONTACTS+"");
117. count = mDB.update(
118. MyContacts.TB_NAME, contentValues,
119. selection, selectionArgs);
120. break;
121. case MyContacts.CONTACT_ID:
122. String contactID = uri.getPathSegments().get(1);
123. Log.d(TAG+"update",contactID+"");
124. count = mDB.update(MyContacts.TB_NAME,contentValues,
125. MyContacts._ID + "=" + contactID,
126. selectionArgs);
127. break;
128. default: throw new IllegalArgumentException(
129. "Unsupported URI: " + uri);
130. }
131. return count;
132. }
133. }
复制代码
MyContentProvider扩展了android.content.ContentProvider类,数据层的操作就在该类中完成。sqlite、文件形式、内存对象,持久化或者非持久化数据存储都可以包装在此类中,本文中使用了sqlite数据库进行持久化存储。
getContext().getContentResolver().notifyChange可以通知观察者数据发生改变,这是处理多应用调用时数据同步的关键,本文并没有完整的展现。自定义的MyContentProvider.java中还实现了update方法,在AcMain中没有调用到,你可尝试一下如何实现。
与MyContentProvider有关的表mycontacts相对应类MyContacts.java,其中定义了唯一资源标示URI,表结构字段名等。任何contentprovider的调用都是通过系统来进行,因此,URI起到了定位资源和特定provider类的作用。
1. //MyContacts.java
2. package jtapp.contentproviders;
3. 4. import android.content.UriMatcher;
5. import android.net.Uri;
6. import android.provider.BaseColumns;
7. 8. public class MyContacts implements BaseColumns {
9. public MyContacts(){
10. }
11. public static final String AUTHORITY =
12. "jtapp.contentproviders.contacts";
13. public static final String TB_NAME = "mycontacts";
14. public static final Uri CONTENT_URI = Uri.parse(
15. "content://" + AUTHORITY + "/" + TB_NAME);
16.
17. public static final int CONTACTS = 1;
18. public static final int CONTACT_ID = 2;
19. public static final UriMatcher uriMatcher;
20. static{
21. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
22. uriMatcher.addURI(AUTHORITY,"mycontacts",CONTACTS);
23. uriMatcher.addURI(AUTHORITY,"mycontacts/#",CONTACT_ID);
24. }
25.
26. public static final String _ID = "id";
27. public static final String NAME = "name";
28. public static final String NUMBER1 = "number1";
29. public static final String EMAIL = "email";
30. }
复制代码
应用配置文件AndroidManifest.xml 代码:
1. <?xml version="1.0" encoding="utf-8"?>
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3. package="jtapp.contentproviders" android:versionCode="1"
4. android:versionName="1.0">
5. <application android:icon="@drawable/icon" android:label="@string/app_name">
6. <activity android:name=".AcMain" android:label="@string/app_name">
7. <intent-filter>
8. <action android:name="android.intent.action.MAIN" />
9. <category android:name="android.intent.category.LAUNCHER" />
10. </intent-filter>
11. </activity>
12. <provider android:authorities="jtapp.contentproviders.contacts"
13. android:name="MyContactsProvider"></provider>
14. </application>
15. </manifest>
复制代码
|