ContentProvider详解及使用大全
Android中的Content provider机制可支持在多个应用中存储和读取数据。这也是跨应用共享数据的唯一方式。在android系统中,没有一个公共的内存区域,供多个应用共享存储数据。
Android提供了一些主要数据类型的Content provider,比如音频、视频、图片和私人通讯录等。可在android.provider包下面找到一些android提供的Content provider。可以获得这些Content provider,查询它们包含的数据,当然前提是已获得适当的读取权限。
如果想公开自己的数据,那么可有两种办法:
创建自己的Content provider,需要继承ContentProvider类;
如果你的数据和已存在的Content provider数据结构一致,可以将数据写到已存在的Content provider中,当然前提是获取写该Content provider的权限。比如把OA中的成员通讯信息加入到系统的联系人Content provider中。
Content provider基础
所有Content provider都需要实现相同的接口用于查询Content provider并返回数据,也包括增加、修改和删除数据。
首先需要获得一个ContentResolver的实例,可通过Activity的成员方法getContentResovler()方法:
ContentResolver cr = getContentResolver();
ContentResolver实例带的方法可实现找到指定的Content provider并获取到Content provider的数据。
ContentResolver的查询过程开始,Android系统将确定查询所需的具体Content provider,确认它是否启动并运行它。android系统负责初始化所有的Content provider,不需要用户自己去创建。实际上,content provider的用户都不可能直接访问到content provider实例,只能通过ContentResolver在中间代理。
数据模型
Content provider展示数据类似一个单个数据库表。其中:
每行有个带唯一值的数字字段,名为_ID,可用于对表中指定记录的定位;
Content provider返回的数据结构,是类似JDBC的ResultSet,在android中,是Cursor对象。
URI
每个content provider定义一个唯一的公开的URI,用于指定到它的数据集。一个content provider可以包含多个数据集(可以看作多张表),这样,就需要有多个URI与每个数据集对应。这些URI要以这样的格式开头:
content://
表示这个uri指定一个content provider。
如果你想创建自己的content provider,最好把自定义的URI设置为类的常量,这样简化别人的调用,并且以后如果更新URI也很容易。android定义了CONTENT_URI常量用于URI,比如:
android.provider.Contacts.Phones.CONTENT_URI android.provider.Contacts.Photos.CONTENT_URI
Cursor cur = managedQuery(myPerson, null, null, null, null);
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");
Cursor cursor = getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) { builder .append( cursor .getString(cursor .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))) .append("-"); }
ContentValues values = new ContentValues();values.put(People.NAME, "Abraham Lincoln");Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
Uri uri=Uri.withAppendedPath(People.CONTENT_URI, "23");Uri 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);
query() insert() update() delete() getType() onCreate()query()方法,返回值是Cursor实例,用于迭代请求的数据。Cursor是一个接口。android为该接口提供了一些只读的(和JDBC的ResultSet不一样,后者还提供可写入的可选特性)Cursor实现。比如SQLiteCursor,可迭代SQLite数据库中的数据。可以通过SQLiteDatabase类的query()方法获取到该Cursor实例。还有其他的Cursor实现,比如MatrixCursor,用于数据不是存储在数据库的情况下。
public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transportationprovider");
<provider android:name="com.easymorse.cp.MyContentProvider" android:authorities="com.easymorse.cp.mycp"></provider>
content://com.easymorse.cp.mycp/emperors表示获取朝代列表集合。而:content://com.easymorse.cp.mycp/emperors/1表示其中的_ID值为1的一个特定的记录。
private static final int ITEMS = 1; private static final int ITEM = 2;另外要创建UriMatcher实例:private static UriMatcher uriMatcher;static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, TABLE_EMPERORS, ITEMS); uriMatcher.addURI(PROVIDER_NAME, TABLE_EMPERORS + "/#", ITEM); }
content://com.easymorse.cp.mycp/emperors
content://com.easymorse.cp.mycp/emperors/##号表示一个id值。
@Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case ITEMS: return "vnd.android.cursor.dir/vnd.easymorse.mycp"; case ITEM: return "vnd.android.cursor.item/vnd.easymorse.mycp"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } }
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch (uriMatcher.match(uri)) { case ITEMS: return database.query(TABLE_EMPERORS, projection, selection, selectionArgs, null, null, sortOrder); case ITEM: return database.query(TABLE_EMPERORS, projection, _ID + "=" + uri.getPathSegments().get(1), selectionArgs, null, null, null); default: throw new IllegalArgumentException("unknown uri: " + uri); } }
private String getContentProviderValues() { StringBuilder builder = new StringBuilder(); // 查名称和朝代,朝代=明,而且按照登基时间倒排序 Cursor cursor = managedQuery(MyContentProvider.CONTENT_URI, new String[] { MyContentProvider.NAME, MyContentProvider.DYNASTY }, MyContentProvider.DYNASTY + "=?", new String[] { "明" }, " start_year desc"); // 查全部记录 // Cursor cursor = managedQuery(MyContentProvider.CONTENT_URI, null, // null, // null, null); // 根据id定位记录(0..1) // Cursor cursor = managedQuery(ContentUris.withAppendedId( // MyContentProvider.CONTENT_URI, 1), // new String[] { MyContentProvider.NAME }, null, null, null); while (cursor.moveToNext()) { builder .append( cursor.getString(cursor .getColumnIndex(MyContentProvider.NAME))) .append(" | ") // .append( // cursor // .getString(cursor // .getColumnIndex(MyContentProvider.START_YEAR))) // .append(" | ") .append( cursor.getString(cursor .getColumnIndex(MyContentProvider.DYNASTY))) .append("\n"); }
@Override public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) { switch (uriMatcher.match(uri)) { case ITEM: return database.update(TABLE_EMPERORS, contentValues, _ID + "=" + uri.getPathSegments().get(1) + " and (" + selection + ")", selectionArgs); case ITEMS: return database.update(TABLE_EMPERORS, contentValues, selection, selectionArgs); default: throw new IllegalArgumentException("unknown uri: " + uri); } }这里复杂的地方是如果根据id做修改,比如还有其他附加条件,需要拼接where子句字符串。后面的and要带括号,否则逻辑可能不同。
ContentValues values = new ContentValues(); values.put(MyContentProvider.NAME, "朱重八"); getContentResolver().update(MyContentProvider.CONTENT_URI, values, MyContentProvider.NAME + "=?", new String[] { "朱元璋" });