一、创建自己的ContentProvider
1.新建一个自定义的Provider去继承ContentProvider类
public class DatabaseProvider extends ContentProvider
2.打开AndroidManifest.xml文件,对自定义的Provider进行如下配置:
<provider
android:name=".DatabaseProvider"
android:authorities="com.xxxx.xxxxx.databasetest.provider"
android:enabled="true"
android:exported="true"></provider>
其中authorities属性值可以自定义,它是内容URI的authority部分,用于区分不同的应用程序,一般为了避免冲突,都会采用程序包名的方式来命名,我这里指定为“包名.provider”。exported属性表示是否允许外部程序访问我们的ContentProvider,Enabled属性表示是否启用该ContentProvider。
3.声明自定义ContentProvider的常量,当然这个并不是必须,只是为了方便使用(样例中只有两张表:Book,Category)
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.xxxx.xxxxx.litepaltest.provider";
4.创建URI匹配器的实例,并为其添加URI
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY,"book",BOOK_DIR);
uriMatcher.addURI(AUTHORITY,"book/#",BOOK_ITEM);
uriMatcher.addURI(AUTHORITY,"category",CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY,"category/#",CATEGORY_ITEM);
}
一个标准的内容URI写法应该是:content://com.example.app.provider/table1 这表示调用方期望访问的是com.example.app这个应用的table1表中的数据。如果我们在这个内容URI后面再加一个数字id,例如:content://com.example.app.provider/table1/1 表示调用方期望访问的是com.example.app这个应用的table1表中id为1的数据。同时我们可以使用通配符的方式来分别匹配这两种格式的内容URI:*表示任意长度的字符,#表示任意长度的数字。
因此,一个能够匹配任意表的内容URI格式可以写成:
content://com.example.app.provider/*
一个能够匹配table1表中任意一行数据的内容URI格式可以写成:
content://com.example.app.provider/table1/#
再看回来,URI匹配器的addURI方法接收三个参数,依次为authority,path和一个自定义代码。这样,当我们在调用URI匹配器的match()方法时,就可以将一个Uri对象传入,返回一个能与之匹配的自定义代码,从而通过判断自定义代码知晓调用方期望访问的是哪一张表的数据了。
5.重写自定义ContentProvider的六个方法
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(),"BookStore.db",null,1);
return true;
}
只有当存在ContentResolver尝试访问我们程序中的数据时,该方法才会调用,用于初始化ContentProvider。通常会在这里完成数据库的创建和升级等操作。
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("Book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("Category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
这里我们先通过DatabaseHelper类获取数据库的操作对象SQLiteDatabase,然后调用其query方法查询数据,返回一个cursor对象。值得注意的是,当我们查询的是单条数据时,我们先通过uri.getPathSegments()方法将内容URI中authority之后的部分以“/”符号分割开,然后取其index为1的部分即为查询的id值,然后再调用query方法即可。
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri uriInsertItem = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book",null,values);
uriInsertItem = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category",null,values);
uriInsertItem = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
break;
default:
break;
}
return uriInsertItem;
}
插入数据时,insert()方法需要返回一个新增数据的URI,因此通过调用Uri.parse()方法将新增数据的内容URI解析成Uri对象并返回。
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updateRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updateRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updateRows = db.update("Book", values, "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
updateRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updateRows = db.update("Category", values, "id = ?", new String[]{categoryId});
break;
default:
break;
}
return updateRows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deleteRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deleteRows = db.delete("Book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deleteRows = db.delete("Book", "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
deleteRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deleteRows = db.delete("Category", "id = ?", new String[]{categoryId});
break;
default:
break;
}
return deleteRows;
}
更新数据的操作方法以及删除数据的操作方法和查询方法类似,不同的是它们返回值是受影响的行数,为int类型。
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.xxxx.xxxxx.databasetest.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.xxxx.xxxxx.databasetest.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.xxxx.xxxxx.databasetest.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.xxxx.xxxxx.databasetest.provider.category";
}
return null;
}
getType()方法用于获取Uri对象所对应的MIME类型。一个内容URI所对应的MIME字符串由三部分组成,Android对这三个部分做了如下格式规定:
a.必须以vnd开头
b.如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则后接android.cursor.item/
c.最后接上vnd.<authority>.<path>
二、跨程序访问共享数据
public class MainActivity extends AppCompatActivity {
private Button btnAdd,btnQuery,btnUpdate,btnDelete;
private static final String URI = "content://com.xxxx.xxxxx.databasetest.provider/book";
private String newId = "";
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(URI);
ContentValues values = new ContentValues();
values.put("name","A Clash of Kings");
values.put("author","George Martin");
values.put("pages",1040);
values.put("price",22.85);
Uri newUri = getContentResolver().insert(uri,values);
newId = newUri.getPathSegments().get(1);
}
});
btnQuery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(URI);
Cursor cursor = getContentResolver().query(uri,null,null,null,null);
if(cursor!=null){
while (cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d(TAG, "book name is: "+name);
Log.d(TAG, "book author is: "+author);
Log.d(TAG, "book pages is: "+pages);
Log.d(TAG, "book price is: "+price);
}
cursor.close();
}
}
});
btnUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(URI+"/"+newId);
ContentValues values = new ContentValues();
values.put("name","A Storm of Swords");
values.put("pages",1216);
values.put("price",24.05);
getContentResolver().update(uri,values,null,null);
}
});
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(URI+"/"+newId);
getContentResolver().delete(uri,null,null);
}
});
}
private void initViews(){
btnAdd = (Button) findViewById(R.id.btn_add);
btnQuery = (Button) findViewById(R.id.btn_query);
btnUpdate = (Button) findViewById(R.id.btn_update);
btnDelete = (Button) findViewById(R.id.btn_delete);
}
}