通过简单的例子通过ContentProvider实现进程间内容读写
服务端
1.服务端用来向外提供数据,首先创建2张表:
public class DbHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "book_provider.db";
public static final String BOOK_TABLE_NAME = "book";
public static final String USER_TALBE_NAME = "user";
private static final int DB_VERSION = 3;
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_TALBE_NAME + "(_id INTEGER PRIMARY KEY, name TEXT,sex INT)";
public DbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
public DbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_BOOK_TABLE);
sqLiteDatabase.execSQL(CREATE_USER_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
在构造方法中执行创建2张表的SQL语句,一张book表,一张user表。
2.新建一个类实现ContentProvider,并实现增删改查的方法
public class RemoteProvider extends ContentProvider {
private static final String TAG = "RemoteProvider";
public static final String AUTHORITY = "com.zhu.ipcserver.provider";
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;
public static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
mUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
mUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
}
private Context mContext;
private SQLiteDatabase mDb;
@Override
public boolean onCreate() {
Log.d(TAG, "onCreate: " + Thread.currentThread().getName());
mContext = getContext();
initProviderData();
return true;
}
private void initProviderData() {
mDb = new DbHelper(mContext).getWritableDatabase();
mDb.execSQL("delete from " + DbHelper.BOOK_TABLE_NAME);
mDb.execSQL("delete from " + DbHelper.USER_TALBE_NAME);
mDb.execSQL("insert into book values(1,'三体');");
mDb.execSQL("insert into book values(2,'人类简史');");
mDb.execSQL("insert into book values(3,'未来简史');");
mDb.execSQL("insert into user values(1,'Jack',1);");
mDb.execSQL("insert into user values(2,'Rose',0);");
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
String tableName = getTableName(uri);
Log.d(TAG, "query: " + tableName + " -- " + Thread.currentThread().getName());
return mDb.query(tableName, strings, s, strings1, null, null, s1, null);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
Log.d(TAG, "getType: " + Thread.currentThread().getName());
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
String table = getTableName(uri);
Log.d(TAG, "insert: " + table + " -- " + Thread.currentThread().getName());
if (TextUtils.isEmpty(table)) {
// throw new IllegalAccessException("Unsupport uri: " + uri);
Log.e(TAG, "insert: tableName is null..");
}
mDb.insert(table, null, contentValues);
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
String tableName = getTableName(uri);
Log.d(TAG, "delete: " + tableName + " -- " + Thread.currentThread().getName());
int count = mDb.delete(tableName, s, strings);
if (count > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
String tableName = getTableName(uri);
Log.d(TAG, "update: " + tableName + " -- " + Thread.currentThread().getName());
int row = mDb.update(tableName, contentValues, s, strings);
if (row > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return row;
}
private String getTableName(Uri uri) {
String tableName = "";
switch (mUriMatcher.match(uri)) {
case BOOK_URI_CODE:
tableName = DbHelper.BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
tableName = DbHelper.USER_TALBE_NAME;
break;
}
return tableName;
}
}
3.在清单文件中声明该组件
<permission
android:name="com.zhu.ipcserver.PROVIDER"
android:protectionLevel="normal" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
<provider
android:name=".RemoteProvider"
android:authorities="com.zhu.ipcserver.provider"
android:exported="true"
android:permission="com.zhu.ipcserver.PROVIDER" />
</application>
到此,该ContentProvider就可以向外部应用提供数据了。
主要内容如下:
- 在onCreate中执行创建表,向表中插入数据的语句
- 在清单文件中声明权限,声明authority来连接匹配该内容提供者
- 因为该类中提供了两个表的操作,因此在static代码块中通过
UriMatcher
把要访问的表与对应的code进行了关联,根据Uri取出code,根据code得到表明,再进行相应的增删改查操作 - 在增删改查中就根据参数,得到的表明进行操作即可
- 需要注意,
update
,insert
,delete
方法会引起数据源的变化,因此需要通过ContentResolver的notifyChange
方法来通知外界当前ContentProvider中的数据发生了改变;也可以通过ContentResolver的registerContentObserver
方法来注册观察者,通过unregisterContentObserver
来解除观察者 -
query,update,insert,delete
四个方法是存在多线程并发访问的,因此方法内部要做好线程同步。
客户端代码
通过authority指定的uri对远程ContentProvider进行访问
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
var bookUri = Uri.parse("content://com.zhu.ipcserver.provider/book")
var userUri = Uri.parse("content://com.zhu.ipcserver.provider/user")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv_insert.setOnClickListener {
var values = ContentValues()
values.put("_id", 5)
values.put("name", "book#5")
contentResolver.insert(bookUri, values)
queryBook(bookUri)
}
queryBook(bookUri)
var userCursor = contentResolver.query(userUri, arrayOf("_id", "name", "sex"), null, null, null)
while (userCursor.moveToNext()) {
var user = User()
user.userId = userCursor.getInt(0)
user.userName = userCursor.getString(1)
user.isMale = userCursor.getInt(0) == 1
Log.d(TAG, "query user: " + user.toString())
}
userCursor.close()
}
private fun queryBook(uri: Uri) {
var cursor = contentResolver.query(uri, arrayOf("_id", "name"), null, null, null)
while (cursor.moveToNext()) {
var book = Book()
book.bookId = cursor.getInt(0)
book.bookName = cursor.getString(1)
Log.d(TAG, "queryBook book: " + book.toString())
}
cursor.close()
}
}
- 首先获取book表,uesr表中的信息
- 在点击事件中插入一条数据后再次查询一遍信息
最后必不可少的一步:权限声明
<uses-permission android:name="com.zhu.ipcserver.PROVIDER" />
- 在清单文件中要申请在server中定义的读写权限,否则会报SecurityException权限Denied错误
测试结果:
客户端:
image.png
首先查询了2张表中的数据
红线部分点击插入了book表中一条数据然后再次查询新增book#5
服务端:
image.png
onCreate方法运行在主线程中
增删改查运行在binder线程池中
表中数据:
image.png
完。【该文demo参考自《Android开发艺术探索》一书】
网友评论