美文网首页
IPC之ContentProvider用法

IPC之ContentProvider用法

作者: 一线游骑兵 | 来源:发表于2018-09-17 14:22 被阅读9次

    通过简单的例子通过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开发艺术探索》一书】

    相关文章

      网友评论

          本文标题:IPC之ContentProvider用法

          本文链接:https://www.haomeiwen.com/subject/rbbknftx.html