关于IPC机制中的几种通讯方式,简单的几种如利用Bundle,文件,以及ContentProvider。ContentProvider的底层实现也是一种,利用ContentProvider实现了封装,让不同进程间的通讯更加简便。下面来说一下ContentProvider的具体用法。
我们在获取手机通讯录号码,短信息等手机保存的信息时,就是通过ContentProvider来实现的。例如手机通讯录号码就是通讯录提供一个provide,我们根据Provide的Uri来获取到信息。具体的实现代码网上一大堆,拿过来就能用。既然我们说到IPC机制,我们实现的方式是在一个进程中提供一个provide来保存信息,在另一个进程中通过getContentResolver()来获取,进行CRUD操作。下面我们自己创建一个provide,然后在另一个进程中调用。
首先,我们来说明如果定义一个ContentProvider以及使用
定义一个MyProvider继承ContentProvider,实现他的6个方法。在清单文件中声明provider,几个关键属性:
name:即provide的具体类名,
authorities:对外的一个Uri,不同的进程通过这个Uri来访问provider,进行一系列操作
permission:权限,也可以单有readPermission和writePermission
process:进程名,默认的为包名,这里我们给provider单起一个进程:":remote"
这样一个自定义个ContentProvider就定义完成了。
在B进程中的Activity中通过getContentResolver()就能调用provider中的几个方法了。
其中的Uri参数为
Uri uri = Uri.parse("content://" + AUTHORITY); // AUTHORITY为我们在清单文件中声明的uri
下面通过一个小例子来理解ContentProvider。
这个例子的大体思路是,定义一个继承自ContentProvider的类用来提供数据,放到一个A进程中,在B进程中的Activity中通过ContentProvider来获取到A进程中的数据。这里我们用SQLite数据库来保存A进程中的数据,然后在B进程中获取。
先定义一个MyOpenHelper类用来操作数据库,继承自SQLiteOpenHelper,代码如下
public class MyOpenHelper extends SQLiteOpenHelper{
private static final int VERSION = 1;
private static final String DB_NAME = "book.db";
public static final String BOOK_TABLE_NAME = "book";
public static final String USER_TABLE_NAME = "user";
private static final String CREATE_BOOK_TABLE = "create table if not exists " + BOOK_TABLE_NAME + "(_id integer primary key, name text)";
private static final String CREATE_USER_TABLE = "create table if not exists " + USER_TABLE_NAME + "(_id integer primary key, name text, sex integer)";
public MyOpenHelper(Context context) {
super(context, DB_NAME, null, 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) {
}
}
定义MyProvider类,用于向外界提供数据
public class MyProvider extends ContentProvider{
private static final String TAG = "MyProvider";
private static final String AUTHORITY = "com.baiyyyhjl.ipcproject.contentprovider.MyProvider";
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
private static final int BOOK_URI_CODE = 0;
private static final int USER_URI_CODE = 1;
// 根据Uri来判断操作哪一个数据库表
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加匹配Uri的code
static {
mUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
mUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
}
private Context mContext;
private SQLiteDatabase database;
@Override
public boolean onCreate() {
Log.d(TAG, "onCreate" + Thread.currentThread().getName());
mContext = getContext();
initProviderData();
return true;
}
private void initProviderData() {
database = new MyOpenHelper(mContext).getWritableDatabase();
// 在下次启动时,先删除数据库中的数据再插入数据
database.execSQL("delete from " + MyOpenHelper.BOOK_TABLE_NAME);
database.execSQL("delete from " + MyOpenHelper.USER_TABLE_NAME);
database.execSQL("insert into book values(3, 'Android')");
database.execSQL("insert into book values(4, 'IOS')");
database.execSQL("insert into book values(5, 'Html5')");
database.execSQL("insert into user values(1, 'jake', 1);");
database.execSQL("insert into user values(3, 'jasmine', 0);");
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.d(TAG, "query" + Thread.currentThread().getName());
String tableName = getTableName(uri);
if (tableName == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
return database.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
}
@Nullable
@Override
public String getType(Uri uri) {
Log.d(TAG, "getType");
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d(TAG, "insert");
String tableName = getTableName(uri);
if (tableName == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
database.insert(tableName, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.d(TAG, "delete");
String tableName = getTableName(uri);
if (tableName == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int count = database.delete(tableName, selection, selectionArgs);
if (count > 0){
mContext.getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.d(TAG, "update");
String tableName = getTableName(uri);
if (tableName == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int row = database.update(tableName, values, selection, selectionArgs);
if (row > 0){
mContext.getContentResolver().notifyChange(uri, null);
}
return row;
}
private String getTableName(Uri uri){
String table = null;
// 根据uri来判断是哪一个表
switch (mUriMatcher.match(uri)){
case BOOK_URI_CODE:
table = MyOpenHelper.BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
table = MyOpenHelper.USER_TABLE_NAME;
break;
default:
break;
}
return table;
}
}
重写他的增删改查方法,调用SQLiteDatabase操作数据库获取数据,唯一需要注意的是我们需要根据Uri来匹配不同的数据库表。
最后在进程B中的MyProviderActivity通过ContentProvider获取其他进程(A进程)中的数据
public class MyProviderActivity extends AppCompatActivity{
private static final String TAG = "MyProviderActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Uri bookUri = MyProvider.BOOK_CONTENT_URI;
ContentValues bookValues = new ContentValues();
bookValues.put("_id", 6);
bookValues.put("name", "android开发");
getContentResolver().insert(bookUri, bookValues);
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
while (bookCursor.moveToNext()){
Book book = new Book();
book.setId(bookCursor.getInt(0));
book.setName(bookCursor.getString(1));
Log.d(TAG, book.toString());
}
bookCursor.close();
Uri userUri = MyProvider.USER_CONTENT_URI;
Cursor userCursor = getContentResolver().query(userUri, new String[]{"_id", "name", "sex"}, null, null, null);
while (userCursor.moveToNext()){
User user = new User();
user.setId(userCursor.getInt(0));
user.setName(userCursor.getString(1));
user.setIsMale(userCursor.getInt(2) == 0);
Log.d(TAG, user.toString());
}
userCursor.close();
}
}
这样,对ContentProvider的创建和使用就完成了。ContentProvider可以方便进行跨进程间的数据传递。