【Android 笔记】ContentProvider 及其实现 IPC
ContentProvider 是 Android 中用于实现不同应用中共享数据的组件。通过 ContentProvider 可以将数据分享给其他应用或进程,但是它不直接存储数据,只提供管理数据的一系列方法,数据的存储可以是 SQLite、文件等。调用者也不是直接使用 ContentProvider 里的方法,而是需要借助 ContentResolver 对象。
即 调用者 ==> ContentResolver ==> ContentProvider ==> 数据
使用 ContentProvider
继承 ContentProvider,实现:
- boolean onCreate()
- Cursor query()
- Uri insert()
- int delete()
- int update()
- String getType(),返回 Uri 请求的 MIME 数据类型
其中 query()、insert()、delete()、update() 可能存在多并发的情况,需要处理好同步问题。
使用 ContentProvider 还需在 Manifest 中注册
- android:authorities,用于唯一标识该 ContentProvider
- exported,boolean值,表示能否被调用
- permission,读写权限,第三方应用要使用该 ConetentProvider 必须声明
- readPermission,query() 权限
- writePermission,insert()、update()、delete() 权限
ContentResolver
调用者使用 ContentProvider 是通过ContentResolver 来实现的,ContentResolver 提供了与 ContentProvider 对应的一系列方法用于对数据的增删改查,它可以调用任何可以调用的 ContentProvider,而识别具体的 ContentProvider 需要通过 Uri
UriMatch
ContentResolver 通过 Uri 来调用 ContentProvider,一个 ContentProvider 中可能可以访问多个表,为了知道 Uri 指的是哪张表,需要使用 UriMatch 来进行匹配。
- void addURI(String authority,String path, int code),添加一个用于匹配的 Uri 以及 Uri 所对应的 code
- int match(Uri uri),uri 匹配成功时,返回 addURI() 中的 code
Demo
实现 Activity 跨进程访问 ContentProvider 插入、删除、查询联系人,数据存储使用 SQLite。
1)实现联系人数据库的管理
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "contacts.db";
public static final String CONTACTS_TABLE_NAME = "t_contacts";
private static final String CREATE_CONTACTS_TABLE = "CREATE TABLE IF NOT EXISTS "
+ CONTACTS_TABLE_NAME + " (phonenumber CHAR(11) PRIMARY KEY, name TEXT)";
public static final int DB_VESION = 1;
public DBOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VESION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_CONTACTS_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
2)继承 ContentProvider,实现对联系人的增删改查等
public class ContactsProvider extends ContentProvider {
private static final String AUTHORITY = "cn.vecrates.ipc.contentProvider.ContactsProvider";
public static final String CONTACTS_URI = "content://" + AUTHORITY + "/" + DBOpenHelper.CONTACTS_TABLE_NAME;
private static final int CONTACTS_TABLE_CODE = 0;
private UriMatcher mUriMatcher; //用于匹配不同的URI,以便操作不同的表
private SQLiteDatabase mDb;
@Override
public boolean onCreate() {
Log.v("_v", "ContactsProvider进程Id " + Process.myPid());
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//使t_contacts 表与 code 联系起来
mUriMatcher.addURI(AUTHORITY, DBOpenHelper.CONTACTS_TABLE_NAME, CONTACTS_TABLE_CODE);
mDb = new DBOpenHelper(getContext()).getWritableDatabase();
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
String tableName = getTableName(uri);
if(tableName == null) {
throw new IllegalArgumentException("Erro Uri: " + uri);
}
Cursor cursor = mDb.query(tableName, strings, s, strings1, s1, null, null);
return cursor;
}
/**
* Uri中的数据的MIME类型
* @param uri
* @return
*/
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
String tableName = getTableName(uri);
if(tableName == null) {
throw new IllegalArgumentException("Erro Uri: " + uri);
}
mDb.insert(tableName, null, contentValues);
getContext().getContentResolver().notifyChange(uri, null);
return null;
}
@Override
public int delete(Uri uri, String s, String[] strings) {
String tableName = getTableName(uri);
if(tableName == null) {
throw new IllegalArgumentException("Erro Uri: " + uri);
}
int count = mDb.delete(tableName, s, strings);
if(count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return 0;
}
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
/**
* 通过此方法判别 URI 是指向那张数据表
* @param uri
* @return
*/
private String getTableName(Uri uri) {
String tableName = null;
switch (mUriMatcher.match(uri)) {
case CONTACTS_TABLE_CODE:
tableName = DBOpenHelper.CONTACTS_TABLE_NAME;
break;
}
return tableName;
}
}
并且注册 ContentProvider
<provider
android:name=".contentProvider.ContactsProvider"
android:authorities="cn.vecrates.ipc.contentProvider.ContactsProvider"
android:permission="cn.vecrates.BOOKPROVIDER"
android:process=":provider"/>
3)通过 ContentResolver 调用
public class ProviderActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_provider);
Log.v("_v", "Activity进程Id " + Process.myPid());
Uri contactsUri = Uri.parse(ContactsProvider.CONTACTS_URI);
// getContentResolver().delete(contactsUri, "name=?", new String[]{"vecrates"});
ContentValues values = new ContentValues();
values.put("phoneNumber", "15000000001");
values.put("name", "vecrates");
getContentResolver().insert(contactsUri, values);
Log.v("_v", "插入了 " + "vecrates " + " 15000000001");
String[] colum = new String[]{"phoneNumber", "name"};
Cursor cursor = getContentResolver().query(contactsUri, colum, null, null, null);
while(cursor.moveToNext()) {
String phoneNumber = cursor.getString(0);
String name = cursor.getString(1);
Log.v("_v", "获取到联系人 " + phoneNumber + " " + name);
}
cursor.close();
}
}