他的底层也是Binder
系统预置了许多ContentProvider,比如通讯录信息,日程表信息等。要跨进程访问这些信息,只需要通过ContentProvider的query、update、insert、delete方法即可。getType用来返回一个Uri请求所对应的MIME类型(媒体类型),比如图片视频等。如果不关注这个,返回null或者*/*即可。根据Binder原理可以得知,这6个方法都运行在ContentProvider进程中。在这个进程中,onCreate由系统回调运行在主线程中,另外5个方法由外界回调,运行在Binder的线程池中。
ContentProvider以表格的形式来组织数据,并且可以包含多个表。对于每个表,他们都具有行和列的层次性,和数据库类似。除了表格的形式,ContentProvider还支持文件数据,比如图片、视频等。文件数据和表格数据的结构不同,因此处理处理这类数据时,可以在ContentProvider中返回文件的句柄给外界从而让文件来访问ContentProvider中的文件信息。Android系统所提供的MediaStore功能就是功能就是文件类型的MediaStore。另外,虽然ContentProvider的底层数据看起来很像一个SQLite数据库,但是ContentProvider对底层的数据存储方式没有任何要求,我们既可以使用SQLite数据库,也可以使用普通的文件,甚至可以采用内存中的一个对象来进行数据存储。
ContentProvider的权限还可以细分为读权限和写权限。
数据库工具类
public class DbOpenHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "book_provider.db";
public static final String BOOK_TABLE_NAME = "book";
public static final String USER_TABLE_NAME = "user";
private static final int DB_VERSION = 1;
private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS" + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS" + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT," + "sex INT)";
public DbOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABLE);
db.execSQL(CREATE_USER_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
provider,uri和数据库表映射通过UriMatcher。通过ContentProvider访问数据库表
public class BookProvider extends ContentProvider {
private static final String TAG = "xbh";
public static final String AUTHORITY = "com.example.test";
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
public static final int BOOK_URI_CODE = 0;
public static final int USER_URI_CODE = 1;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
}
private Context mContext;
private SQLiteDatabase mDb;
public BookProvider() {
}
@Override
public boolean onCreate() {
Log.i(TAG, "onCreate: ");
mContext = getContext();
initProviderData();
return true;
}
private void initProviderData() {
mDb = new DbOpenHelper(mContext).getWritableDatabase();
mDb.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME);
mDb.execSQL("delete from " + DbOpenHelper.USER_TABLE_NAME);
mDb.execSQL("insert into book values(3, 'Android');");
mDb.execSQL("insert into book values(4, 'IOS');");
mDb.execSQL("insert into book values(5, 'HTML5');");
mDb.execSQL("insert into book values(1, 'Jake', 1);");
mDb.execSQL("insert into book values(2, 'Jasmine', 0);");
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
String table = getTableName(uri);
if (table == null){
throw new IllegalArgumentException("no this uri");
}
return mDb.query(table, projection, selection, selectionArgs, null, null, sortOrder, null);
}
@Override
public String getType(Uri uri) {
Log.i(TAG, "getType: ");
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i(TAG, "insert: ");
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("no this uri");
}
mDb.insert(table, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i(TAG, "delete: ");
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("no this uri");
}
int count = mDb.delete(table, selection, selectionArgs);
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.i(TAG, "update: ");
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("no this uri");
}
int row = mDb.update(table, values, selection, selectionArgs);
if (row > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return row;
}
private String getTableName(Uri uri) {
String tableName = null;
switch (sUriMatcher.match(uri)) {
case BOOK_URI_CODE:
tableName = DbOpenHelper.BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
tableName = DbOpenHelper.USER_TABLE_NAME;
break;
default:
break;
}
return tableName;
}
}
注意点:query、update、insert、delete是可以多线程并发访问的,所以需要做好线程同步。具体原因是SQLiteDatabase内部对数据库的操作是由同步处理的。但是如果通过多个SQLiteDatabase对象来操作数据库就无法进行同步了。如果ContentProvider底层是一块内存的话,比如是List,在这种情况下同List的遍历、插入、删除操作就需要进行线程同步,否则就会引起并发错误。
使用代码
public class ProviderActivity extends AppCompatActivity {
private static final String TAG = "xbh";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_provider);
Uri uri = Uri.parse("content://com.example.test");
getContentResolver().query(uri, null, null, null, null);
getContentResolver().query(uri, null, null, null, null);
getContentResolver().query(uri, null, null, null, null);
Uri bookUri = Uri.parse("content://com.example.test/book");
ContentValues values = new ContentValues();
values.put("_id", 6);
values.put("name", "程序设计的艺术");
getContentResolver().insert(bookUri, values);
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
while (bookCursor.moveToNext()) {
Book book = new Book();
book.bookId = bookCursor.getInt(0);
book.bookName = bookCursor.getString(1);
Log.i(TAG, book.toString());
}
bookCursor.close();
}
}