ContentProvider-内容提供者是android中四大组件之一,可见是比较重要的。他可以在不同app之间(进程之间)提供数据共享,且可以进行增删改查,当然在自己进程里也同样可以的。要实现数据共享,必定离不开ContentResolver-内容解决者。ContentProvider内部实现了增删改查的方法,并向外提供了若干个URI;ContentResolver也有增删改查方法,各方法都需传入URI,通过URI就可以找到正确的ContentProvider,并执行对应的方法。
自定义内容提供者,需继承ContentProvider抽象类,并重写增删改查方法,其中数据需要保存到数据库中,必定需要用到SQLite数据库,android创建数据库一般都用SQLiteOpenHelper。以下是测试代码,TestContentProvider.java:
package com.test.contprovider;
import java.util.ArrayList;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.text.TextUtils;
public class TestContentProvider extends ContentProvider {
/*外部通过这个类拿URI及相关字段等常量*/
public static class TestWords{
/*内容提供者主机头,全局唯一,要跟AndroidMainifast.xml注册的一致*/
public static final String AUTHORITY = "com.test.content.provider";
/*外部调用时的URI*/
public static final Uri CONTENT_ALL_URI = Uri.parse("content://"+AUTHORITY+"/all");
public static final Uri CONTENT_ONE_URI = Uri.parse("content://"+AUTHORITY+"/one");
/*数据库表字段*/
public static final String COLUMN_ID = "_id";
public static final String COLUMN_WORD = "word";
}
private TestSQLiteOpenHelper sqlhelper;
private String TABLE = "words";
/*URI匹配器,检查外部调用的是哪个URI*/
private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static int ALL = 1;
private static int ONE = 2;
static{
matcher.addURI(TestWords.AUTHORITY, "all", ALL);
/* ‘#’号代表可以是任意数字,通常是_id字段值 */
matcher.addURI(TestWords.AUTHORITY, "one/#", ONE);
}
@Override
public boolean onCreate() {
/*创建数据库words.db*/
sqlhelper = new TestSQLiteOpenHelper(getContext(), "words.db", null,1);
return true ;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db
= sqlhelper.getWritableDatabase();
//匹配uri
if(matcher.match(uri)==ALL){
return db.query(TABLE, projection, selection, selectionArgs, null, null, sortOrder);
}else if(matcher.match(uri)==ONE){
long _id = ContentUris.parseId(uri);
//设置查询条件
String where = TestWords.COLUMN_ID +"=" + _id + " ";
if(!TextUtils.isEmpty(selection)){
where+=selection;
}
return db.query(TABLE, projection, where, selectionArgs, null, null, sortOrder);
}
return null;
}
@Override
public String getType(Uri uri) {
//…………
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = sqlhelper.getWritableDatabase();
long _id = db.insert(TABLE, "_id", values);
if(_id>0){
uri = ContentUris.withAppendedId(uri, _id);
//通知内容解决者,数据发生改变
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//…………
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
//…………
return 0;
}
/*批量更新,可以提高执行效率,可以跟进源码父类applyBatch方法没有进行事务处理
*为了保正操作一致性,可以重写该方法,加上事务处理
*/
@Override
public ContentProviderResult[] applyBatch(ArrayList operations)
throws OperationApplicationException{
SQLiteDatabase db = sqlhelper.getWritableDatabase();
//开始事务
db.beginTransaction();
try{
ContentProviderResult[] results = super.applyBatch(operations);
//设置事务标记为successful
db.setTransactionSuccessful();
return results;
}finally {
//结束事务
db.endTransaction();
}
}
public class TestSQLiteOpenHelper extends SQLiteOpenHelper{
public TestSQLiteOpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
/*创建表words,字段两个:_id , word */
String sql = "create table words(_id integer primary key autoincrement,word text) ";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
android四大组件都需要在AndroidManifest.xml中注册,当然ContentProvider也不例外。
简单的注册如上就可以了。当然了注册时还牵涉到访问权限的问题,还需待进一步研究。
内容解决者可以对感兴趣的内容提供者进行监听,当有数据发生改变时,可以收到通知,当然了这需要内容提供者发送通知才行 getContext().getContentResolver().notifyChange(uri, null);
注册收到通知代码如下:
getContentResolver().registerContentObserver(
TestWords.CONTENT_ONE_URI, true,
new ContentObserver(handler) {
@Override
public boolean deliverSelfNotifications() {
System.out.println("--------deliverSelfNotification-----");
return super.deliverSelfNotifications();
}
@Override
public void onChange(boolean selfChange) {
System.out.println("--------onChange-----"+selfChange);
super.onChange(selfChange);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
System.out.println("--------onChange-----"+selfChange+","+uri);
}
});
内容解决者可以访问内容提供者暴露出来的URI,进行数据共享操作。在Activity中获取ContentResolver:
ContentResolver cr = getContentResolver();
/*非自己进程写入URI*/
//Uri uri = Uri.parse("content://com.test.content.provider/all");
/*自己进程中获取URI*/
Uri uri = TestWords.CONTENT_ALL_URI;
Cursor cursor = cr.query(uri, null, null, null, null);
void batchAdd(){
ArrayList ops = new ArrayList();
ops.add(ContentProviderOperation.newInsert(TestWords.CONTENT_ONE_URI)
.withValue(TestWords.COLUMN_WORD, "one")
.build());
ops.add(ContentProviderOperation.newInsert(TestWords.CONTENT_ONE_URI)
.withValue(TestWords.COLUMN_WORD, "two")
.build());
ops.add(ContentProviderOperation.newInsert(TestWords.CONTENT_ONE_URI)
.withValue(TestWords.COLUMN_WORD, "three")
.build());
ContentResolver cr = getContentResolver();
try {
ContentProviderResult[] cpr = cr.applyBatch(TestWords.AUTHORITY, ops);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (OperationApplicationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}