Android四大组件-ContentProvider

概念:

内容提供器,Android 四大组件之一。

作用:

可以操作非本应用程序的数据,实现跨进程数据共享。

优点:

可以在保证数据的安全性的情况下实现数据跨进程共享。因为contentprovider规范了统一的数据访问接口。

对底层数据存储方式抽象,即如果您将底层数据存储方式修改对数据应用层不会有影响。

原理:

底层实现原理是Binder机制,Binder实现原理是通过Binder类,实现IBinder接口。Binder机制原理是:Binder驱动在内核空间创建一块数据缓存区,并调用系统mmap()方法实现内存映射,发送进程调用系统方法发送数据到虚拟内存区域,由于内核缓存区和接收进程空间地址存在映射关系,所以也就相当于发送到了接收进程的用户空间地址,实现了跨进程通信。 发送进程(client)和接收进程(server)通过ServiceManager与Binder驱动沟通。

用法:
  • 通过contentresolver进行增删改查操作。使用contentresolver的原因:可以统一管理provider
  • 通过URI表示需要获取数据的表名,uri的固定形式:content://Authority/path/id
    • Authority:授权信息,用以区别不同的contentprovider。
    • path:表名,用以区分contentprovider的不同的数据表。
    • ID:id号,用以区别表中不同的数据。
  • UriMatchar:固定格式,单条记录:vnd.android.cursor.item/自定义,多条记录:vnd.android.cursor.dir/自定义
用法示例1:

读取手机中的通讯录信息,涉及运行时权限申请详见官方文档:https://developer.android.com/training/permissions/requesting?hl=zh-cn

//读取通讯录主要逻辑代码
private void readContact(){
   
    Cursor cursor = null;
    try{
     //获取contentresolver
         ContentResolver resolver = context.getContentResolver();
         // //查询通信录的数据
         cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
         //遍历数据获取具体字段
            if (cursor!=null){
                while (cursor.moveToNext()){
                    String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    contcacts.add(name+"\n"+number);
                }
                adapter = new ArrayAdapter(mActivity, android.R.layout.simple_list_item_activated_1, contcacts);
                lv.setAdapter(adapter);
            }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        if(cursor!=null){
            cursor.close();
        }
    }
}
用法示例2:

实现跨进程数据共享。步骤如下:

  • 创建好数据库

  • 在进程A中创建Provider,注意需要注册,并且设置可以对外。

  • 在provider中定义好UriMatcher和authorty(与B进程通信的链接)。

  • 实现6大方法。

  • 在B进程通过上下文获取contentresolver,通过resolver对A进程数据库进程增删改查操作。

进程A:创建一个继承ContentProvider的类,并实现重写的方法。

**注:**示例中的UserDao是封装的一个对sqlite数据库增删改查的操作的类。具体可详见github上的demo。https://github.com/MarinaTsang/sqliteAndContentprovider/tree/master/app

/**
 * 实现跨进程数据共享
 */

public class MyProvider extends ContentProvider {

    //自定义的urimatcher 中的自定义代码
    private static final int TABLE1_DIR = 0;
    private static final int TABLE1_ITEM = 1;

    //自定义uri规则
    private static final String AUTHORITY = "com.example.zeng.contentprovider.provider";

    private static UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, MySqlHelper.TABLE_NAME,TABLE1_DIR);
        uriMatcher.addURI(AUTHORITY,MySqlHelper.TABLE_NAME+"/#",TABLE1_ITEM);
    }


    private UserDao userDao;
    /**
     * 初始化 内容提供器  ,此方法内一般进行数据库的创建和升级操作。  运行在content
     * provider的主线程中,故不能做耗时操作。
     * @return   true ---初始化成功   false---初始化失败
     */
    @Override
    public boolean onCreate() {
        userDao = new UserDao(getContext());
        return true;
    }

    /**
     * 查询
     * @param uri    uri,固定格式,用于标识读取哪里的数据
     * @param projection  需要查询的列的数组,null表示所有列
     * @param selection    需要过滤的条件 where语句,null返回所有行
     * @param selectionArgs   where的参数
     * @param sortOrder      对结果进行排序
     * @return
     */
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        Cursor cursor= null;
        switch (uriMatcher.match(uri)){
            case TABLE1_DIR:
                //查询表1 所有数据
                cursor = userDao.query(projection, selection, selectionArgs, sortOrder);
                break;
            case TABLE1_ITEM:
                //查询表1 单条数据
                String queryId = uri.getPathSegments().get(1);
                cursor = userDao.query(projection,"uid= ?",new String[]{queryId},sortOrder);
                break;

        }

        return cursor;
    }

    /**
     * uri是内容解析者传递过来的:contentresolver
     * 根据传入的URI来返回对应的 MIME类型,  两种类型:vnd.android.cursor.item/  单条记录,  uri 以ID结尾         vnd.android.cursor.dir/   多条记录
     * @return
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)){
            case TABLE1_DIR:
                return "vnd.android.cursor.dir/vnd."+AUTHORITY+"."+MySqlHelper.TABLE_NAME;
            case TABLE1_ITEM:
                return "vnd.android.cursor.item/vnd."+AUTHORITY+"."+MySqlHelper.TABLE_NAME;
        }

        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Uri uriRetrun = null;
        switch (uriMatcher.match(uri)){
            case TABLE1_DIR:
            case TABLE1_ITEM:
                long l = userDao.addDatasWithValues(values);
                uriRetrun = Uri.parse("content://"+AUTHORITY+"/"+MySqlHelper.TABLE_NAME+"/"+l);
        }
        return uriRetrun;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return userDao.deleteData(selection,selectionArgs);
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return userDao.updateDataWithVaules(values,selection,selectionArgs);
    }
}

进程B使用contentresolver获取数据:
用法与用法示例1 获取手机通信录方法一致。

完整demo地址:https://github.com/MarinaTsang/sqliteAndContentprovider

参考文章:
https://www.jianshu.com/p/06309249f2a0
https://developer.android.com/reference/android/content/ContentProvider
等。

猜你喜欢

转载自blog.csdn.net/MarinaTsang/article/details/84566897