一篇就够了系列之ContentProvider全解析

前言:

上一篇介绍广播接收者(BroadcastReceover),本篇继续,记叙下Android四大组件最后一个不常用,但是非常有用的ContentProvider。Google文档

本文主要从以下几个方面介绍:

  • 特点
  • 优缺点
  • 数据访问
  • 创建内容提供器
  • 工作机制

特点

  1. Android四大组件之一,需要进行注册,一般有name,authorities,export等属性
  2. 是一种定义数据共享的接口,并是不android数据存储的方式之一
  3. 跨进程数据访问
  4. android系统很多系统应用都是使用ContentProvider方式进行储存的(图片,通讯录,视频,音频等)
  5. 数据更新监听方便

优缺点

优点:

  1. 为数据访问提供统一的接口,解决了不同储存方式需要不同的API的访问所带来的繁琐
  2. 跨进程数据的访问,实现了不同App数据访问提供了很大的便利

缺点:

  1. 不能单独使用,必须需要和其他的储存方式结合使用

数据访问

下面示例如何获取通讯录信息:
Activity:

public class ContentProviderActivity extends AppCompatActivity {

    private ContentResolver mContentResolver;//
    StringBuilder builder=new StringBuilder("");
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content_provider);
        mContentResolver=getContentResolver();
        tv= (TextView) findViewById(R.id.tv_show_contract);
        findViewById(R.id.tv_press_contract).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            //动态申请权限
                if(ContextCompat.checkSelfPermission(ContentProviderActivity.this, android.Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
                    ActivityCompat.requestPermissions(ContentProviderActivity.this,new String[]{Manifest.permission.READ_CONTACTS},1);
                }else{
                    readContacts();
                }
            }
        });
    }

    private void readContacts(){
        Cursor cursor=null;
        try {
            cursor=mContentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
            if(cursor!=null){
                while(cursor.moveToNext()){
                    String displayName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    builder.append(displayName);
                    builder.append(": ");
                    builder.append(number);
                    builder.append("   ");
                    Log.i("wy",displayName+"   "+number);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(cursor!=null){
                cursor.close();
            }
            tv.setText(builder);
        }
    }
}

以上:

  • 借助ContentResolver类访问内容提供器中共享的数据,其提供了一系列的增删改查(CRUD)操作,其中insert方法用于添加数据,update更新数据,delete删除数据,query用于查询数据。和SQLiteDatabase类似,只是参数上稍微有些区别,ContentResolver中接收的参数是不接收表名参数的,而是使用一个Uri参数代码(关于Uri内容欢迎参考一篇就够了系列之Activity全解析中关于Scheme的介绍)。
  • 由于联系人信息是危险权限,所以需要动态申请,同时manifest中也需要申明
  • 代码中的查询等方法可以看到是个常量,其实也源码的一个封装。

创建内容提供器

public class MyContentProvider extends ContentProvider {

    public static final int DIR1=0;
    public static final int ITEM1=1;
    public static final int DIR2=2;
    public static final int ITEM2=3;

    public static final String AUTHRITY="com.wenyi.interview.provider";
    private static UriMatcher uriMatcher;
    private MyDatabaseHelper dbhelper;

    static {
        uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHRITY,"book",DIR1);
        uriMatcher.addURI(AUTHRITY,"book/#",ITEM1);
        uriMatcher.addURI(AUTHRITY,"category",DIR2);
        uriMatcher.addURI(AUTHRITY,"category/#",ITEM2);
    }


    /**
     * 初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作
     * @return true表示初始化成功,false表示初始化失败
     * 只有当contentResolver尝试访问我们的程序中的数据是,内容提供器才会被初始化
     */
    @Override
    public boolean onCreate() {
        dbhelper=new MyDatabaseHelper(getContext(),"MyStore.db",null,2);
        return true;
    }

    /**
     * 查询数据
     * @param uri 确定查询哪张表
     * @param projection 确定查询哪些列
     * @param selection 约束查询哪些行
     * @param selectionArgs 约束查询哪些行
     * @param sortOrder 用于对结果进行排序,查询的结果存放在Cursor对象中返回
     * @return
     */
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        SQLiteDatabase db=dbhelper.getReadableDatabase();
        Cursor cursor=null;
        switch (uriMatcher.match(uri)){
            case DIR1:
                cursor=db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case ITEM1:
                String bookid=uri.getPathSegments().get(1);
                cursor=db.query("Book",projection,"id=?",new String[]{bookid},null,null,sortOrder);
                break;
            case DIR2:
                cursor=db.query("Category",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case ITEM2:
                String categoryid=uri.getPathSegments().get(1);
                cursor=db.query("Category",projection,"id=?",new String[]{categoryid},null,null,sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    /**
     *
     * @param uri
     * @return 传入的内容URI来返回相应的MIME类型
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)){
            case DIR1:
                return "vnd.android.cursor.dir/vnd.com.wenyi.interview.provider.book";
            case ITEM1:
                return "vnd.android.cursor.item/vnd.com.wenyi.interview.provider.book";
            case DIR2:
                return "vnd.android.cursor.dir/vnd.com.wenyi.interview.provider.category";
            case ITEM2:
                return "vnd.android.cursor.item/vnd.com.wenyi.interview.provider.category";
            default:
                break;
        }
        return null;
    }

    /**
     * 添加数据
     * @param uri 确定要添加到的表
     * @param values 待添加的数据
     * @return 添加完成后,返回一个用于表示这条心记录的URI
     */
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    /**
     * 删除数据
     * @param uri 确定删除哪一张表中的数据
     * @param selection 约束删除哪些行
     * @param selectionArgs 约束删除哪些行
     * @return 被删除的行
     */
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    /**
     * 更新数据
     * @param uri 更新的表
     * @param values 新数据保存在其中
     * @param selection 约束更新的行
     * @param selectionArgs 约束更新的行
     * @return 受影响的行作为返回值
     */
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

代码有点长,现在来细细分析:

  1. 自定义MyContentProvider继承自ContentProvider,重写相应的方法,方法都有解释
  2. 四个常量分别表示访问Book表中的说有数据,访问Book表中的单条数据,访问Category表中的所有数据和访问Category表中的单条数据
  3. 在manifest中进行注册,<provider
    android:exported="true"
    android:enabled="true"
    android:authorities="com.wenyi.interview.provider"
    android:name=".FourComponet.contentProvider.MyContentProvider"/>
  4. 代码中,UriMatche类,可以用来配皮Uri类型数据,其中,“*”和“#”分别表示匹配任意长度的任意字符和任意长度的数字。
  5. 创建MyDatabaseHelper对象,继承自SQLiteOpenHelper,方便数据可操作,因为内容提供着只是接口标准,需要和数据存储方式结合使用
  6. 只在query中写了相应代码,其他方法也需要写类似代码
  7. getType方法中,返回值很奇特。实际上,这是MIME字符格式,MIME主要由3部分组成,在Android中,格式为: a: 必须以vnd开头 b: 如果内容Uri以路径结尾,则后面接 android.cursor.dir/。如果内容Uri以id结尾,则后面接android.cursor.item/ c: 在最后面接 vnd.authority.path
  8. 这样,我们的内容提供器就创建完成了,通过这种机制,必须知道URI,才能顺利的访问该数据库中的内容,比保证了可访问,又保证了安全性。

工作机制

暂不分析

欢迎阅读其他系列文章:

一篇就够了系列之Activity全解析

一篇就够了系列之Service全解析

一篇就够了系列之BroadcastReceiver全解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章