IPC之ContentProvider用法

通过简单的例子通过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.在清单文件中声明该组件
  
  
        ...
        
    

到此,该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表中的信息
  • 在点击事件中插入一条数据后再次查询一遍信息
最后必不可少的一步:权限声明
    
  • 在清单文件中要申请在server中定义的读写权限,否则会报SecurityException权限Denied错误

测试结果:

客户端:


IPC之ContentProvider用法_第1张图片
image.png

首先查询了2张表中的数据
红线部分点击插入了book表中一条数据然后再次查询新增book#5

服务端:


IPC之ContentProvider用法_第2张图片
image.png

onCreate方法运行在主线程中
增删改查运行在binder线程池中
表中数据:


IPC之ContentProvider用法_第3张图片
image.png

完。【该文demo参考自《Android开发艺术探索》一书】

你可能感兴趣的:(IPC之ContentProvider用法)