Android 开发的四大组件(四)-Content Provider

152 阅读5分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战

上一篇文章,我们没有按照介绍的顺序介绍Content Provider,而是介绍了广播BroadCast,那么这篇文章,我们来介绍最后一个组件,Content Provider,它作为Android开发的四大组件,肯定是很重要的,虽然在开发过程中很少使用,但是不代表就不需要知道,那么我们开始吧。

Content Provider的定义

ContentProvider怎么说呢,说它是数据库嘛,又不只是增删改查那么简单的,说它是Binder,又不只是跨进程,所以它是什么呢,ContentProvider是为不同的应用之间实现数据共享,提供统一的接口,也就是说ContentProvider可以实现进程间的数据共享,实现跨进程通信。那么它能干嘛呢?

1.封装数据,提供统一接口,当项目需求需要修改数据源的时候,节省时间和人力

2.提供一种跨进程数据共享的方式

3.数据更新通知机制。

Content Provider的用法及功能

对于Content Provider我们可以使用系统自带的用法,也可以自定义一个Content Provider来使用。 获取Content Provider的基本方法类似数据库,得到contentResolver类对象:ContentResolver cr = getContentResolver(),然后定义要查询的字段String数组,在使用Query方法返回一个cursor对象,通过遍历获取cursoe里面的内容。

上面我们提到Content Provider实现了数据共享的功能,那么它是怎么实现数据共享的呢,Content Provider通过将我们获取或者保存的数据通过Uri的方式进行数据共享。上面还说的,它类似数据库,但是它不是数据库,它不仅可以进行本应用的数据增删改查,还可以实现不同App直接的数据共享,同时还可以增删改查本地文件和XML文件。而数据库只能增删改查本应用下的数据。

Content Provider的使用

Content Provider和其他组件一样,也是需要在清单文件里面进行注册

 <provider
            android:authorities="com.test.provider"
            android:name=".MyContentProvider"
            android:exported="true"/>

既然它的主要功能是实现不同应用间的数据共享,那么我们就针对不同应用间做数据更新,看看效果: A应用做数据处理,B应用做数据更新:
A应用:

public class MyContentProvider extends ContentProvider {

    private String TAG = "MyContentProvider";

    //这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities,这里的authorities可以随便写
    private static final String AUTHORITY = "com.test.provider";
    //匹配成功后的匹配码
    private static final int MATCH_ALL_CODE = 1;
    private static final int MATCH_ONE_CODE = 2;

    private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/test");


    private static final UriMatcher uriMatcher;

    //在静态代码块中添加要匹配的 Uri
    static {
        //匹配不成功返回NO_MATCH(-1)
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        /**
         * uriMatcher.addURI(authority, path, code); 其中
         * authority:主机名(用于唯一标示一个ContentProvider,这个需要和清单文件中的authorities属性相同)
         * path:路径路径(可以用来表示我们要操作的数据,路径的构建应根据业务而定)
         * code:返回值(用于匹配uri的时候,作为匹配成功的返回值)
         */
        uriMatcher.addURI(AUTHORITY, "test", MATCH_ALL_CODE);// 匹配记录集合
        uriMatcher.addURI(AUTHORITY, "test/#", MATCH_ONE_CODE);// 匹配单条记录
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        switch (uriMatcher.match(uri)) {
            /**
             * 这里如果匹配是uriMatcher.addURI(AUTHORITY, "test",
             * MATCH_SUCCESS_CODE);中的Uri,则查询全部
             *
             */
            case MATCH_ALL_CODE:
                Log.d(TAG, "queryAll");
                break;
            /**
             * 这里如果匹配是uriMatcher.addURI(AUTHORITY,
             * "test/#",MATCH_ONE_CODE);中的Uri,查询一个
             */
            case MATCH_ONE_CODE:
                break;
            default:
        }
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    /**
     * 插入 使用UriMatch的实例中的match方法对传过来的 Uri进行匹配。 这里通过ContentResolver传过来一个Uri,
     * 用这个传过来的Uri跟在ContentProvider中静态代码块中uriMatcher.addURI加入的Uri进行匹配
     * 根据匹配的是否成功会返回相应的值,在上述静态代码块中调用uriMatcher.addURI(AUTHORITY,
     * "test",MATCH_CODE)这里的MATCH_CODE
     * 就是匹配成功的返回值,也就是说假如返回了MATCH_CODE就表示这个Uri匹配成功了
     */
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        switch (uriMatcher.match(uri)) {
            case MATCH_ALL_CODE:
                String provider = (String) values.get("provider");
                Log.d(TAG, "insertAll--" + provider);
                //通知ContentObserver数据发生变化了
                notifyDataChanged();
                break;
            case MATCH_ONE_CODE:
                break;
            default:
        }
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        switch (uriMatcher.match(uri)) {
            case MATCH_ALL_CODE:
                Log.d(TAG, "deleteAll");
                break;
            case MATCH_ONE_CODE:
                break;
            default:
        }
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        switch (uriMatcher.match(uri)) {
            case MATCH_ALL_CODE:
                Log.d(TAG, "updateAll");
                break;
            case MATCH_ONE_CODE:
                break;
            default:
        }
        return 0;
    }

    //通知指定URI数据已改变
    private void notifyDataChanged() {
        getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
    }
}

再来看B应用:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private String TAG = "provider";
    private static final String AUTHORITY = "com.test.provider";

    private static final Uri URI = Uri.parse("content://" + AUTHORITY + "/test");
    private ContentResolver contentResolver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
    }

    private void initData() {
        contentResolver = getContentResolver();
        //注册内容观察者
        contentResolver.registerContentObserver(URI, true, new MyContentObserver(null));
    }

    private void initView() {
        findViewById(R.id.add).setOnClickListener(this);
        findViewById(R.id.delete).setOnClickListener(this);
        findViewById(R.id.update).setOnClickListener(this);
        findViewById(R.id.query).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(TAG, "provider test");
        //通过内容解析者操作内容观察者
        switch (v.getId()) {
            case R.id.add:
                contentResolver.insert(URI, contentValues);
                break;
            case R.id.delete:
                contentResolver.delete(URI, null, null);
                break;
            case R.id.update:
                contentResolver.update(URI, contentValues, null, null);
                break;
            case R.id.query:
                contentResolver.query(URI, null, null, null, null);
                break;
        }

    }


    //定义内容观察者
    class MyContentObserver extends ContentObserver {


        public MyContentObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            Log.d(TAG, "改变了");
        }
    }
}

运行上面代码,可以看到,由于应用A的insert方法,通知了ContentObserver,所以应用B可以收到数据改变的通知。

/provider: insertAll--provider
/provider: 改变了
/MyContentProvider: deleteAll
/MyContentProvider: updateAll
/MyContentProvider: queryAll

相对于其他三个组件,Content Provider的使用没有那么简单,但是很清楚易懂,正常的观察者模式,主要是进程间的通信和数据共享,如果项目中有类似的需求,可以考虑使用。

Content Provider使用过程中的注意事项,最重要的就是开始定义的authorities,一定要统一,保持一致,否是就会报错

安卓开发的四大组件,最后一个也介绍完了,按照惯例,下一篇就是总结啦,欢迎莅临指导指正,留言评论,谢谢