还没弄懂四大组件?带你全面解析ContentProvider篇

喜欢的小伙伴欢迎关注,我会定期分享Android知识点及解析,还会不断更新的BATJ面试专题,欢迎大家前来探讨交流,如有好的文章也欢迎投稿。

创新互联专注于企业营销型网站建设、网站重做改版、双河网站定制设计、自适应品牌网站建设、H5响应式网站商城网站制作、集团公司官网建设、成都外贸网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为双河等各大城市提供网站开发制作服务。

1.什么是ContentProvider

ContentProvider,是自身APP开放给第三方APP的,用于访问自身数据库数据的接口。
第三方APP可以通过该接口,对指定的数据进行增删改查。

那么如何定义自身的ContentProvider接口呢?

在回答问题之前,先来关注一下Uri。

2.什么是Uri

原因在于,uri是ContentProvider解析外部请求(或者说是,第三方访问自身数据库)的关键参数。

Uri的字符串格式如下

content://package/table/id

例如

content://com.breakloop.sqlitehelperdemo/hero/1

从上方Uri示例中,可以获取到以下信息。

第三方APP想要

  1. 访问com.breakloop.sqlitehelperdemo的数据库。至于哪个,由contentProvider内部映射指定。

  2. 访问表hero。至于表名是不是真为hero,也由contentProvider说了算。

  3. 访问id为1的数据。至于是不是id代表的具体含义,解释权也归contentProvider

那么,第三方APP将Uri传入后,ContentProvider如何将其map为具体的数据库操作呢?

这便有了UriMatcher工具类的引入。

3.UriMatcher

该工具类,可以将Uri映射为int类型的行为代码。行为代码,可以看做是ContentProvider自定义的枚举类型。而不同的行为代码,绑定不同的数据库操作。

我们先来看一下,Uri与行为代码的映射关系

    public final static String AUTHORITY="com.breakloop.contentproviderdemo1";    public final static int BY_NAME=1;    public final static int BY_AGE=2;    public final static int BY_SEX=3;    public final static int BY_NONE=0;    public final static String PATH_BY_NAME=DBConst.TABLE_PERSON+"/ByName/*";    public final static String PATH_BY_AGE=DBConst.TABLE_PERSON+"/ByAge/#";    public final static String PATH_BY_SEX=DBConst.TABLE_PERSON+"/BySex/*";
}
static {
    matcher=new UriMatcher(UriMatcher.NO_MATCH);
    matcher.addURI(MatcherConst.AUTHORITY,MatcherConst.PATH_BY_NAME,MatcherConst.BY_NAME);
    matcher.addURI(MatcherConst.AUTHORITY,MatcherConst.PATH_BY_AGE,MatcherConst.BY_AGE);
    matcher.addURI(MatcherConst.AUTHORITY,MatcherConst.PATH_BY_SEX,MatcherConst.BY_SEX);
    matcher.addURI(MatcherConst.AUTHORITY,DBConst.TABLE_PERSON,MatcherConst.BY_NONE);
}

在上面的示例中,UriMatch绑定了四个Uri,并将各个Uri映射为四个行为代码。

其中,用到了转义符。#代表任意数字,*代表任意字母。

那么如何将行为代码映射为具体的数据库操作呢?,换句话说,在哪儿使用UriMatcher呢?当然是ContentProvider中!!!在ContentProvider中的增删改查方法中,完成操作映射。

我们来看一下,ContentProvider的创建。

4.ContentProvider的创建

先用Android Studio 创建一个ContentProvider.

还没弄懂四大组件?带你全面解析ContentProvider篇

创建过程中,需要提供AUTHORITY,

还没弄懂四大组件?带你全面解析ContentProvider篇

ContentProvider生成后,Android Studio将自动帮助ContentProvider在Manifest中进行注册。

还没弄懂四大组件?带你全面解析ContentProvider篇

接下来,我们看看ContentProvider的结构。

5.ContentProvider的结构

import android.content.ContentProvider;import android.content.ContentValues;import android.content.UriMatcher;import android.database.Cursor;import android.net.Uri;public class MyContentProvider extends ContentProvider {    public MyContentProvider() {
    }    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }    @Override
    public String getType(Uri uri) {        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }    @Override
    public Uri insert(Uri uri, ContentValues values) {        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }    @Override
    public boolean onCreate() {        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

一共七个方法。包括一个构造方法,四个数据库方法(增删改查),一个初始化方法(onCreate),还有一个getType。

关于getType,我们之后解释。

关于构造方法,没什么可解释的。

关于初始化方法,当返回true时,表明初始化成功,否则,失败。由于我们要对数据库进行操作,因此,需要获取sqlite数据库对象。

public boolean onCreate() {
    helper=new MySQLiteHelper(getContext(),DBConst.DB_NAME,null,1);    return true;
}

关于数据库方法,我们看到传参中存在Uri,因此,这里需要用到UirMatcher了。我们将刚才的UriMatcher代码段,加入MyContentProvider.这样,我们就可以在各个数据库方法中,解析Uri了。同时,由于sqlite数据库对象的存在,进而可以对数据库进行相应操作。

6.行为代码Mapping数据库操作

我们先看一下最简单的插入操作。

    public Uri insert(Uri uri, ContentValues values) {
        Uri returnUri=null;
        SQLiteDatabase db=helper.getWritableDatabase();        switch (matcher.match(uri)){            case MatcherConst.BY_NONE:                long recordID=db.insert(DBConst.TABLE_PERSON,null,values);
                returnUri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/"+recordID);                break;            default:                break;
        }        return returnUri;
    }

再来看一下稍微复杂的查询。

    public Cursor query(Uri uri, String[] projection, String selection,                        String[] selectionArgs, String sortOrder) {
        Cursor cursor=null;
        SQLiteDatabase db=helper.getReadableDatabase();        switch (matcher.match(uri)){            case MatcherConst.BY_NONE:
                cursor=db.query(DBConst.TABLE_PERSON,projection,selection,selectionArgs,null,null,sortOrder);                break;            case MatcherConst.BY_AGE:
                cursor=db.query(DBConst.TABLE_PERSON,projection,DBConst.COLUMN_AGE+"=?",new String[]{uri.getPathSegments().get(2)},null,null,sortOrder);                break;            case MatcherConst.BY_SEX:
                cursor=db.query(DBConst.TABLE_PERSON,projection,DBConst.COLUMN_SEX+"=?",new String[]{uri.getPathSegments().get(2)},null,null,sortOrder);                break;            case MatcherConst.BY_NAME:
                cursor=db.query(DBConst.TABLE_PERSON,projection,DBConst.COLUMN_NAME+"=?",new String[]{uri.getPathSegments().get(2)},null,null,sortOrder);                break;            default:                break;
        }        return cursor;
    }

这里需要注意的是,如何取Uri中的传入数据。使用的获取方法是uri.getPathSegments().get(index)。该方法获取的是AUTHORITY后面的String部分。
然后,以”/”为分隔符,生成String[]。

接着是更新操作。

 @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {        int recordID=0;
        SQLiteDatabase db=helper.getWritableDatabase();        switch (matcher.match(uri)){            case MatcherConst.BY_NONE:
                recordID=db.update(DBConst.TABLE_PERSON,values,null,null);                break;            case MatcherConst.BY_AGE:
                recordID=db.update(DBConst.TABLE_PERSON,values,DBConst.COLUMN_AGE+"=?",new String[]{uri.getPathSegments().get(2)});                break;            case MatcherConst.BY_SEX:
                recordID=db.update(DBConst.TABLE_PERSON,values,DBConst.COLUMN_SEX+"=?",new String[]{uri.getPathSegments().get(2)});                break;            case MatcherConst.BY_NAME:
                recordID=db.update(DBConst.TABLE_PERSON,values,DBConst.COLUMN_NAME+"=?",new String[]{uri.getPathSegments().get(2)});                break;            default:                break;
        }        return recordID;
    }

还有删除。

    public int delete(Uri uri, String selection, String[] selectionArgs) {        int recordID=0;
        SQLiteDatabase db=helper.getWritableDatabase();        switch (matcher.match(uri)){            case MatcherConst.BY_NONE:
                recordID=db.delete(DBConst.TABLE_PERSON,null,null);                break;            case MatcherConst.BY_AGE:
                recordID=db.delete(DBConst.TABLE_PERSON,DBConst.COLUMN_AGE+"=?",new String[]{uri.getPathSegments().get(2)});                break;            case MatcherConst.BY_SEX:
                recordID=db.delete(DBConst.TABLE_PERSON,DBConst.COLUMN_SEX+"=?",new String[]{uri.getPathSegments().get(2)});                break;            case MatcherConst.BY_NAME:
                recordID=db.delete(DBConst.TABLE_PERSON,DBConst.COLUMN_NAME+"=?",new String[]{uri.getPathSegments().get(2)});                break;            default:                break;
        }        return recordID;
    }

那么,第三方如何调用ContentProvider呢?

7.ContentProvider的使用

这里,我们新建一个工程contentproviderdemo2,在必要的位置使用方法

public ContentResolver getContentResolver()

获取ContentProvider实例,之后便可传入Uri,调用数据库相关方法了。

例如,我们插入四条记录。

        Uri returnUri=null;

        ContentResolver resolver=getContentResolver();
        Uri uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        ContentValues values=new ContentValues();

        values.put(DBConst.COLUMN_NAME,"A");
        values.put(DBConst.COLUMN_AGE,10);
        values.put(DBConst.COLUMN_SEX,"Male");
        returnUri=resolver.insert(uri,values);        if(returnUri!=null)
        Log.i(TAG, "return Uri = "+returnUri.toString());

        values.put(DBConst.COLUMN_NAME,"B");
        values.put(DBConst.COLUMN_AGE,11);
        values.put(DBConst.COLUMN_SEX,"Male");
        returnUri=resolver.insert(uri,values);        if(returnUri!=null)
        Log.i(TAG, "return Uri = "+returnUri.toString());

        values.put(DBConst.COLUMN_NAME,"C");
        values.put(DBConst.COLUMN_AGE,12);
        values.put(DBConst.COLUMN_SEX,"Female");
        returnUri=resolver.insert(uri,values);        if(returnUri!=null)
        Log.i(TAG, "return Uri = "+returnUri.toString());

        values.put(DBConst.COLUMN_NAME,"D");
        values.put(DBConst.COLUMN_AGE,13);
        values.put(DBConst.COLUMN_SEX,"Female");
        returnUri=resolver.insert(uri,values);        if(returnUri!=null)
        Log.i(TAG, "return Uri = "+returnUri.toString());

我们看一下输出结果。

I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/1I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/2I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/3I/com.breakloop.contentproviderdemo2.MainActivity: return Uri = content://com.breakloop.contentproviderdemo1/PERSON/4

既然,写入成功,那我们查询一下。

    public void selectRecord(){
        Uri uri;
        String name="A";        int age=11;
        String sex="Male";

        Log.i(TAG, "Select by Name");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByName/"+name);
        selectRecord(uri);

        Log.i(TAG, "Select by Age");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByAge/"+age);
        selectRecord(uri);

        Log.i(TAG, "Select by Sex");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/BySex/"+sex);
        selectRecord(uri);

        Log.i(TAG, "Select All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        selectRecord(uri);
    }    private void selectRecord(Uri uri){
        Cursor cursor;
        cursor=resolver.query(uri,new String[]{DBConst.COLUMN_AGE, DBConst.COLUMN_NAME, DBConst.COLUMN_SEX},null,null,null);        if(cursor!=null){            while(cursor.moveToNext()){
                Log.i(TAG, "name = "+cursor.getString(1)+ " age = "+cursor.getInt(0) +" "+cursor.getString(2));
            }
        }
    }

输出结果如下

I/com.breakloop.contentproviderdemo2.MainActivity: Select by Name
I/com.breakloop.contentproviderdemo2.MainActivity: name = A age = 10 Male
I/com.breakloop.contentproviderdemo2.MainActivity: Select by Age
I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male
I/com.breakloop.contentproviderdemo2.MainActivity: Select by Sex
I/com.breakloop.contentproviderdemo2.MainActivity: name = A age = 10 Male
I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male
I/com.breakloop.contentproviderdemo2.MainActivity: Select All
I/com.breakloop.contentproviderdemo2.MainActivity: name = A age = 10 Male
I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male
I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 12 Female
I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 13 Female

我们再来更新一下。

    public void updateRecord(){
        Uri uri;
        String name="A";        int age=11;
        String sex="Female";
        ContentValues values=new ContentValues();

        Log.i(TAG, "Update by Name");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByName/"+name);
        values.put(DBConst.COLUMN_NAME,name+name);
        update(uri,values);

        Log.i(TAG, "Update by Age");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByAge/"+age);
        values.clear();
        values.put(DBConst.COLUMN_AGE,14);
        update(uri,values);

        Log.i(TAG, "Update by Sex");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/BySex/"+sex);
        values.clear();
        values.put(DBConst.COLUMN_AGE,15);
        update(uri,values);

        Log.i(TAG, "Update All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        values.put(DBConst.COLUMN_SEX,"Male");
        update(uri,values);

        Log.i(TAG, "Select All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        selectRecord(uri);
    }    private void update(Uri uri,ContentValues values){        int count;
        count=resolver.update(uri,values,null,null);
        Log.i(TAG, "update "+count+" record");
    }

结果如下

I/com.breakloop.contentproviderdemo2.MainActivity: Update by Name
I/com.breakloop.contentproviderdemo2.MainActivity: update 1 record
I/com.breakloop.contentproviderdemo2.MainActivity: Update by Age
I/com.breakloop.contentproviderdemo2.MainActivity: update 1 record
I/com.breakloop.contentproviderdemo2.MainActivity: Update by Sex
I/com.breakloop.contentproviderdemo2.MainActivity: update 2 record
I/com.breakloop.contentproviderdemo2.MainActivity: Update All
I/com.breakloop.contentproviderdemo2.MainActivity: update 4 record
I/com.breakloop.contentproviderdemo2.MainActivity: Select All
I/com.breakloop.contentproviderdemo2.MainActivity: name = AA age = 15 Male
I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 15 Male
I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 15 Male
I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 15 Male

都不要了,删除!(这里是对更新前的数据库进行的操作)

    public void deleteRecord(){
        Uri uri;
        String name="A";        int age=11;
        String sex="Female";

        Log.i(TAG, "Delete by Name");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByName/"+name);        delete(uri);

        Log.i(TAG, "Select All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        selectRecord(uri);

        Log.i(TAG, "Delete by Age");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/ByAge/"+age);        delete(uri);

        Log.i(TAG, "Select All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        selectRecord(uri);

        Log.i(TAG, "Delete by Sex");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON+"/BySex/"+sex);        delete(uri);

        Log.i(TAG, "Select All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        selectRecord(uri);

        Log.i(TAG, "Delete All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);        delete(uri);

        Log.i(TAG, "Select All");
        uri=Uri.parse("content://"+MatcherConst.AUTHORITY+"/"+DBConst.TABLE_PERSON);
        selectRecord(uri);
    }    private void delete(Uri uri){        int count;
        count=resolver.delete(uri,null,null);
        Log.i(TAG, "delete "+count+" record");
    }

结果如下

I/com.breakloop.contentproviderdemo2.MainActivity: delete 1 record
I/com.breakloop.contentproviderdemo2.MainActivity: Select All
I/com.breakloop.contentproviderdemo2.MainActivity: name = B age = 11 Male
I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 12 Female
I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 13 Female
I/com.breakloop.contentproviderdemo2.MainActivity: Delete by Age
I/com.breakloop.contentproviderdemo2.MainActivity: delete 1 record
I/com.breakloop.contentproviderdemo2.MainActivity: Select All
I/com.breakloop.contentproviderdemo2.MainActivity: name = C age = 12 Female
I/com.breakloop.contentproviderdemo2.MainActivity: name = D age = 13 Female
I/com.breakloop.contentproviderdemo2.MainActivity: Delete by Sex
I/com.breakloop.contentproviderdemo2.MainActivity: delete 2 record
I/com.breakloop.contentproviderdemo2.MainActivity: Select All
I/com.breakloop.contentproviderdemo2.MainActivity: Delete All
I/com.breakloop.contentproviderdemo2.MainActivity: delete 0 record
I/com.breakloop.contentproviderdemo2.MainActivity: Select All

至此,ContentProvider的创建和使用便介绍完了。

如果,只是为了使用,可以只看到这里就好了!


之后的部分,本文还未找到答案!因此,只是记录!
如果有知道的朋友,还望指点。
那么,还有什么?

还记得ContentProvider中的getType吗?
到目前为止,我们并没有实现getType方法。而这并没有对ContentProvider的使用造成任何影响。那么问题来了。getType干嘛用的?什么时候用?

对于getType的解释,网上有两种。

1. 用于对返回数据的解析。判断是一条数据,还是多条数据。若getType方法被实现,则按照实现方法解析,提高了工作效率。否则,系统自动解析。
2. 避免new Intent(String action, Uri uri)方式无法启动activity。
对于解释1,并未从找到官方的出处。而对于解释2,不太明白!我用ContentProvideractivity何事?

关于getType方法的实现,其原型注释给出了答案。

还没弄懂四大组件?带你全面解析ContentProvider篇

方法返回的是MIME类型的String形式。根据Uri的结尾不同,输出也不同。
若Uri以Path结尾,则返回格式为

vnd.android.cursor.dir/vnd..

若Uri以id结尾,则返回格式为

vnd.android.cursor.item/vnd..

因此,我们实例中的getType()实现为

    @Override
    public String getType(Uri uri) {
        Log.i(TAG, "getType: ");        String authorityAndPath=MatcherConst.AUTHORITY+"."+DBConst.TABLE_PERSON;        String handerPath="vnd.android.cursor.dir/vnd.";        String handerID="vnd.android.cursor.item/vnd.";        switch (matcher.match(uri)){            case MatcherConst.BY_NONE:                return handerPath+authorityAndPath;            case MatcherConst.BY_AGE:            case MatcherConst.BY_SEX:            case MatcherConst.BY_NAME:                return handerID+authorityAndPath;            default:                return null;
        }
    }

8.总结

  • 本文主要介绍了Android中四大组件的ContentProvider的一些知识,觉得文章不错的喜欢的小伙伴可以关注加分享,也欢迎大家前来探讨交流。


文章题目:还没弄懂四大组件?带你全面解析ContentProvider篇
链接地址:http://ybzwz.com/article/gijgod.html