Android中的ContentProvider是一种多应用数据共享的机制,任何时候同一Provider只会创建一次,是由系统进行初始化和管理的。本文中将通过实现一个简单通讯录的插入、删除、查询操作来让你了解ContentProvider机制极其自定义过程。读过本系列(读取手机中通讯录)和(sqlite数据库操作)的读者可以发现,本文中实现的应用以这两篇内容为基础的。虽然,本文的MyContentProvider使用sqlite数据库进行持久化存储操作,包装后以ContentProvider机制供各app调用。但是,为理解ContentProvider,你必须有这样两个概念:一、它是一种可以跨应用共享数据的机制,正如本文开头所说。二、底层的存储在实现你的ContentProvider的时候可以自定义,即可以为数据库、也可以为文件,持久化或非持久化存储的其他形式。 程序截图如下: 类AcMain.java,为主Activity,用来实现应用的主界面控件和流程,代码:
- //AcMain.java
- package jtapp.contentproviders;
-
- import android.app.Activity;
- import android.content.ContentValues;
- import android.database.Cursor;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.TextView;
-
- public class AcMain extends Activity {
- private Button btInsertData = null;
- private Button btViewData = null;
- private Button btDelOne = null;
- private Button btClearAll = null;
-
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- btInsertData = (Button) findViewById(R.id.Button02);
- btInsertData.setOnClickListener(new ClickViewHandler());
- btViewData = (Button) findViewById(R.id.Button03);
- btViewData.setOnClickListener(new ClickViewHandler());
- btDelOne = (Button) findViewById(R.id.Button04);
- btDelOne.setOnClickListener(new ClickViewHandler());
- btClearAll = (Button) findViewById(R.id.Button05);
- btClearAll.setOnClickListener(new ClickViewHandler());
- }
-
- public class ClickViewHandler implements OnClickListener {
- @Override
- public void onClick(View v) {
- if (v == btInsertData) {
- InsertSomeRecords();
- } else if (v == btViewData) {
- ViewRecords();
- } else if (v == btDelOne) {
- DelOne();
- } else if (v == btClearAll) {
- DelAllPeople();
- }
- }
-
- private void DelAllPeople() {
- getContentResolver().delete(MyContacts.CONTENT_URI, null, null);
- }
-
- private void DelOne() {
- int id;
- Cursor c = getContentResolver().query(
- MyContacts.CONTENT_URI, null,
- null, null, MyContacts.NAME + " ASC");
- if (c.moveToFirst()) {
- int idxID = c.getColumnIndex(MyContacts._ID);
- id = c.getInt(idxID);
- getContentResolver().delete(MyContacts.CONTENT_URI,
- MyContacts._ID + " = " + id, null);
- }
- }
-
- private void ViewRecords() {
- // Make the query
- Cursor c = managedQuery(MyContacts.CONTENT_URI, null, null, null,
- MyContacts._ID);
- StringBuilder sbRecords = new StringBuilder("");
- if (c.moveToFirst()) {
- int idxID = c.getColumnIndex(MyContacts._ID);
- int idxName = c.getColumnIndex(MyContacts.NAME);
- int idxNumber = c.getColumnIndex(MyContacts.NUMBER1);
- int idxEmail = c.getColumnIndex(MyContacts.EMAIL);
- // Iterator the records
- do {
- sbRecords.append(c.getInt(idxID));
- sbRecords.append(". ");
- sbRecords.append(c.getString(idxName));
- sbRecords.append(", ");
- sbRecords.append(c.getString(idxNumber));
- sbRecords.append(", ");
- sbRecords.append(c.getString(idxEmail));
- sbRecords.append("/n");
- } while (c.moveToNext());
- }
- c.close();
- // Refresh the content of TextView
- ((TextView)(findViewById(
- R.id.TextView01))).setText(sbRecords);
- }
-
- private void InsertSomeRecords() {
- ContentValues values = new ContentValues();
- values.put(MyContacts.NAME, "朱元璋");
- values.put(MyContacts.NUMBER1, "13965625585");
- getContentResolver().insert(MyContacts.CONTENT_URI, values);
- values.clear();
- values.put(MyContacts.NAME, "玄烨");
- values.put(MyContacts.EMAIL, "xueye1772@gmail.com");
- getContentResolver().insert(MyContacts.CONTENT_URI, values);
- }
- }
- }
复制代码
代码中通过getContentResolver()调用contentprovider的delete、insert等操作,由系统根据uri决定调用哪个provider。 query查询除了用getContentResolver().query以外,还可以用activity.managedQuery调用,后者的Cursor的生命周期是由activity来自动管理,本文中的query就是属于这种情况。 主界面ui定义,main.xml,代码:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TextView android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:text="@string/hello" />
- <TableRow android:id="@+id/TableRow01" android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <Button android:text="insert some records" android:id="@+id/Button02"
- android:height="30px" android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </TableRow>
- <TableRow android:id="@+id/TableRow01" android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <Button android:text="delete one" android:id="@+id/Button04"
- android:height="30px" android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <Button android:text="clear all" android:id="@+id/Button05"
- android:height="30px" android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </TableRow>
- <Button android:text="view records" android:id="@+id/Button03"
- android:height="30px" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:layout_gravity="center" />
- <TextView android:text="..." android:id="@+id/TextView01"
- android:layout_width="wrap_content" android:layout_height="wrap_content" />
- </LinearLayout>
复制代码
MyContentProvider.java,代码:
- package jtapp.contentproviders;
- import android.content.ContentProvider;
- import android.content.ContentUris;
- import android.content.ContentValues;
- import android.content.Context;
- import android.database.Cursor;
- import android.database.SQLException;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteQueryBuilder;
- import android.database.sqlite.SQLiteStatement;
- import android.net.Uri;
- import android.text.TextUtils;
- import android.util.Log;
-
- public class MyContactsProvider extends ContentProvider {
- private static final String TAG = "MyContactsProvider";
- private static SQLiteDatabase mDB;
-
- private static void createTablesIfNotExists() {
- mDB.execSQL("CREATE TABLE IF NOT EXISTS "
- + MyContacts.TB_NAME + " ("
- + MyContacts._ID + " INTEGER PRIMARY KEY,"
- + MyContacts.NAME + " VARCHAR,"
- + MyContacts.NUMBER1 + " VARCHAR,"
- + MyContacts.EMAIL + " VARCHAR)");
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- int count;
- switch (MyContacts.uriMatcher.match(uri)) {
- case MyContacts.CONTACTS:
- count = mDB.delete(MyContacts.TB_NAME,
- selection, selectionArgs);
- break;
- case MyContacts.CONTACT_ID:
- String contactID = uri.getPathSegments().get(1);
- count = mDB.delete(MyContacts.TB_NAME,
- MyContacts._ID + "=" + contactID, selectionArgs);
- break;
- default: throw new IllegalArgumentException(
- "Unsupported URI: " + uri);
- }
- return count;
- }
-
- @Override
- public String getType(Uri uri) {
- switch (MyContacts.uriMatcher.match(uri)) {
- case MyContacts.CONTACTS:
- return "vnd.android.cursor.dir/vnd.jtapp.contacts";
- case MyContacts.CONTACT_ID:
- return "vnd.android.cursor.item/vnd.ambow.contacts";
- default:
- throw new IllegalArgumentException("Unsupported URI: " + uri);
- }
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues contentValues) {
- long rowId = mDB.insert(MyContacts.TB_NAME, null, contentValues);
- if (rowId > 0) {
- Uri noteUri =
- ContentUris.withAppendedId(MyContacts.CONTENT_URI,rowId);
- getContext().getContentResolver().notifyChange(noteUri, null);
- Log.d(TAG+"insert",noteUri.toString());
- return noteUri;
- } else {
- throw new SQLException("Failed to insert row into " + uri);
- }
- }
-
- @Override
- public boolean onCreate() {
- if (mDB == null) {
- mDB = this.getContext().openOrCreateDatabase(MyContacts.TB_NAME,
- Context.MODE_PRIVATE, null);
- createTablesIfNotExists();
- }
- return mDB != null;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(MyContacts.TB_NAME);
-
- switch (MyContacts.uriMatcher.match(uri)) {
- case MyContacts.CONTACT_ID:
- qb.appendWhere(
- MyContacts._ID + "=" + uri.getPathSegments().get(1));
- break;
- }
- String orderBy;
- if (TextUtils.isEmpty(sortOrder)) {
- orderBy = MyContacts._ID;
- } else {
- orderBy = sortOrder;
- }
- Cursor c = qb.query(mDB, projection, selection,
- selectionArgs,null, null,orderBy);
- return c;
- }
-
- @Override
- public int update(Uri uri, ContentValues contentValues, String selection,
- String[] selectionArgs) {
-
- Log.d(TAG+"update",contentValues.toString());
- Log.d(TAG+"update",uri.toString());
-
- int count;
- switch (MyContacts.uriMatcher.match(uri)) {
- case MyContacts.CONTACTS:
- Log.d(TAG+"update",MyContacts.CONTACTS+"");
- count = mDB.update(
- MyContacts.TB_NAME, contentValues,
- selection, selectionArgs);
- break;
- case MyContacts.CONTACT_ID:
- String contactID = uri.getPathSegments().get(1);
- Log.d(TAG+"update",contactID+"");
- count = mDB.update(MyContacts.TB_NAME,contentValues,
- MyContacts._ID + "=" + contactID,
- selectionArgs);
- break;
- default: throw new IllegalArgumentException(
- "Unsupported URI: " + uri);
- }
- return count;
- }
- }
-
复制代码
MyContentProvider扩展了android.content.ContentProvider类,数据层的操作就在该类中完成。sqlite、文件形式、内存对象,持久化或者非持久化数据存储都可以包装在此类中,本文中使用了sqlite数据库进行持久化存储。 getContext().getContentResolver().notifyChange可以通知观察者数据发生改变,这是处理多应用调用时数据同步的关键,本文并没有完整的展现。自定义的MyContentProvider.java中还实现了update方法,在AcMain中没有调用到,你可尝试一下如何实现。 与MyContentProvider有关的表mycontacts相对应类MyContacts.java,其中定义了唯一资源标示URI,表结构字段名等。任何contentprovider的调用都是通过系统来进行,因此,URI起到了定位资源和特定provider类的作用。
- //MyContacts.java
- package jtapp.contentproviders;
-
- import android.content.UriMatcher;
- import android.net.Uri;
- import android.provider.BaseColumns;
-
- public class MyContacts implements BaseColumns {
- public MyContacts(){
- }
- public static final String AUTHORITY =
- "jtapp.contentproviders.contacts";
- public static final String TB_NAME = "mycontacts";
- public static final Uri CONTENT_URI = Uri.parse(
- "content://" + AUTHORITY + "/" + TB_NAME);
-
- public static final int CONTACTS = 1;
- public static final int CONTACT_ID = 2;
- public static final UriMatcher uriMatcher;
- static{
- uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- uriMatcher.addURI(AUTHORITY,"mycontacts",CONTACTS);
- uriMatcher.addURI(AUTHORITY,"mycontacts/#",CONTACT_ID);
- }
-
- public static final String _ID = "id";
- public static final String NAME = "name";
- public static final String NUMBER1 = "number1";
- public static final String EMAIL = "email";
- }
-
复制代码
应用配置文件AndroidManifest.xml 代码:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="jtapp.contentproviders" android:versionCode="1"
- android:versionName="1.0">
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".AcMain" android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <provider android:authorities="jtapp.contentproviders.contacts"
- android:name="MyContactsProvider"></provider>
- </application>
- </manifest>
复制代码
主要是里边插入了provider标签,向系统声明了自定义Provider的实现。 |