android contentprovider简单讲解

来源:互联网 发布:mac物理地址 编辑:程序博客网 时间:2024/05/01 23:27

文章的开头奉送上代码方便大家对照学习。

1 ContentProvider简介

ContentProvider是android4大组件之一,它的出现是为了在二个应用之间共享数据。

在Android中,对数据的保护是很严密的,除了放在SD卡中的数据,一个应用所持有的数据库、文件、等等内容,都是不允许其他直接访问的。但有时候沟通是必要的,不仅对第三方很重要,对应用自己也很重要。比如,一个联系人管理的应用。如果不允许第三方的应用对其联系人数据库进行增删该查,整个应用就失去了可扩展力,必将被其他应用抛弃,然后另立门户,自个玩自个的去了。Andorid当然不会真的把每个应用都做成一座孤岛,它为所有应用都准备了一扇窗,这就是Content Provider。

Content Provider屏蔽了内部数据的存储细节,向外提供了上述统一的接口模型。这样大大简化了我们代码的书写。

在使用ContentProvider时我们需要用到以下工具,这里先做简单介绍,下面会仔细讲解:

类 功能 Uri 外部应用唯一确定要访问的ContentProvider ContentResolver 外部应用用来获取ContentProvider对象,进行增删改查操作 UriMatcher ContentUris ContentProvider 给外部应用提供接口,用统一的方式获取共享数据

2 ContentProvider相关类的介绍

这些类大家看不懂也不要紧,大体看一下心里有个印象就行。我们在contentProvider实例讲解用到的时候直接到这里面查阅就行。

2.1 Uri介绍

外部应用通过uri来唯一标识一个ContentProvider。
举个例子:
这里写图片描述

由图中可以看出uri分3部分:

1.scheme:content://
固定写法,表示这个uri指定一个contentprovider(即我们要访问的是一个contentprovider)。

2.主机名(或叫Authority)用于唯一标识一个contentyprovider。注意这个主机名必须和menifest中contentProvider的authorities属性一样,不然找不到我们需要的contentyprovider。在本例子中主机名是:com.ljq.provider.personprovider

3.路径用来表示我们要操作的数据,路径的构建应根据业务而定。例如:

/person/10:要操作person表中id为10的记录person/10/name:要操作person表中id为10的记录的name字段, /person:要操作person表中的所有记录/xxx:要操作xxx表中的记录

如果你想创建自己的contentprovider,最好把自定义的URI设置为常量,这样简化别人的调用,并且以后如果更新URI也很容易。android定义了CONTENT_URI常量用于URI,比如:
public static final Uri CONTENT_URI =
Uri.parse(“content://contacts/people”);
可能大家看到这里还是不知道uri怎么用,我学习的时候也有这个疑问,不要着急,uri要结合UriMatcher和ContentUris一块使用的,下面会讲到。

2.2 ContentResolver

在外部应用的activity中通过getContentResolver()方法来获取对象,然后进行增删改查操作,步骤如下:
1.获取ContentResolver。

getContentResolver()

2.利用ContentResolver增删改查:

增:getContentResolver().insert(Uri url, ContentValues values);删:getContentResolver().delete(Uri url, String where,  String[] selectionArgs)改:getContentResolver().update(Uri uri,  ContentValues values,  String where, String[] selectionArgs)查:getContentResolver().query(Uri uri,  String[] projection,  String selection,  String[] selectionArgs,  String sortOrder);

在这4个方法中,每一个方法都需要一个uri,因为这个uri唯一标识一个contentyprovider(内部是怎么实现的我们下面会讲到,这里不做介绍)。其他的参数就和sqlite操作的参数大同小异,这里不做介绍。

2.3 UriMatcher

UriMatcher用来匹配uri的作用。UriMatcher中只有3个方法(包括构造方法):

1.UriMatcher(int code)方法
构造方法,固定写法:new UriMatcher(UriMatcher.NO_MATCH)

2.addURI(String authority, String path, int code)方法
添加匹配规则

参数 介绍 authority 主机名 path 路径 code 匹配码,当匹配成功后,就会返回这个码

这里对path讲一下,因为它比较灵活,”*”表于匹配所有的字符串,”#” 表示匹配所有数字,如:

    uri的路径是: “/people”    sURIMatcher.addURI("contacts", "people", PEOPLE);    uri的路径是: “/people/10”    sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID);    uri的路径是: “/people/10/phones”    sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES);    uri的路径是: “/people/10/phones/3”    sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID);

3.match(Uri uri)方法
根据addURI()方法添加的匹配规则来匹配,如果匹配到了就会返回响应的匹配码。如:

 int matchId= sURIMatcher.match(uri); switch(matchId){    case 1://表示访问的student表    break;    case 2://表示访问的是teacher表    break;}

2.4 ContentProvider

终于讲到我们的重点了,使用步骤如下:
1.继承ContentProvider重写它的方法:

onCreate()query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)getType(Uri uri)insert(Uri uri, ContentValues values)delete(Uri uri, String selection, String[] selectionArgs) update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

在ContentProvider中,我们要给UriMatcher添加匹配规则,做匹配操作。
2.在menifenst中注册:

<providerandroid:name=".contentprovider.MyContentProvider"android:authorities="com.czh.mycontentprovider.study" />

注意android:authorities是主机名,唯一标识这个ContentProvider,外部应用也是通过这个来查找。

2.5 补充ContentUris

ContentUris是一个辅助工具类,里面全是静态方法,我们看一下:

方法 介绍 appendId(Uri.Builder builder, long id) 在路径后添加id parseId(Uri contentUri) 将路径的最后一个参数转化成long数据,一般用来获取id withAppendedId(Uri contentUri, long id) 在uri后添加id

3 ContentProvider实例

呼!终于讲到实例了。接下来我们自定义ContentProvider来访问数据库。

第一步重写contentProvider:

public class MyContentProvider extends ContentProvider {    private final static int MyStudyTableFlag = 1;//查询表的标记,建议大家都弄成常量    private final static int MyStudyTableNumFlag = 2;//查询表的标记,建议大家都弄成常量    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);    static{//静态语句块,当第一次加载的时候调用        //匹配查询表        sURIMatcher.addURI(Constant.Authorities, Constant.TableName, MyStudyTableFlag);        //匹配查询表中的特定记录        sURIMatcher.addURI(Constant.Authorities, Constant.TableName+"/#", MyStudyTableNumFlag);    }    @Override    public boolean onCreate() {        Log.d(Constant.TAG,"--MyContentProvider--onCreate--");        return false;    }    @Nullable    @Override    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {        Log.d(Constant.TAG,"--MyContentProvider--query--");        int matchId= sURIMatcher.match(uri);        switch(matchId) {            case MyStudyTableFlag://如果查询的是表                SQLiteDatabase db1 = SqlManager.getInstance(getContext()).getWritableDatabase();//                String selectSql1 = "select * from " + Constant.TableName;                Cursor cursor1 = db1.query(true, Constant.TableName, projection,                        selection, selectionArgs, null, null, null, null);                return cursor1;            case MyStudyTableNumFlag://如果查询的是表中的确定id的记录                long id = ContentUris.parseId(uri);//ContentUris的使用。                Log.d(Constant.TAG, "ContentUris:" + id);                SQLiteDatabase db2 = SqlManager.getInstance(getContext()).getWritableDatabase();                //查找id相匹配的数据。                String selectSql2 = "select * from " + Constant.TableName + " where _id = ?";                String[] args = {"" + id};                Cursor cursor2 = db2.rawQuery(selectSql2, args);                return cursor2;        }        return null;    }    @Nullable    @Override    public String getType(Uri uri) {        Log.d(Constant.TAG,"--MyContentProvider--getType--");        return null;    }    @Nullable    @Override    public Uri insert(Uri uri, ContentValues values) {        SQLiteDatabase db = SqlManager.getInstance(getContext()).getWritableDatabase();        db.insert(Constant.TableName,null,values);        Log.d(Constant.TAG,"--MyContentProvider--insert--");        return uri;    }    @Override    public int delete(Uri uri, String selection, String[] selectionArgs) {        Log.d(Constant.TAG,"--MyContentProvider--delete--");        int matchId= sURIMatcher.match(uri);        switch(matchId) {            case MyStudyTableFlag://                SQLiteDatabase db1 = SqlManager.getInstance(getContext()).getWritableDatabase();//                String selectSql1 = "select * from " + Constant.TableName;                db1.delete(Constant.TableName, selection, selectionArgs);                break;            case MyStudyTableNumFlag:                long id = ContentUris.parseId(uri);//ContentUris的使用。                Log.d(Constant.TAG, "ContentUris:" + id);                SQLiteDatabase db2 = SqlManager.getInstance(getContext()).getWritableDatabase();                String[] args = {"" + id};                db2.delete(Constant.TableName, "_id = ?", args);            break;        }        return 0;    }    @Override    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {        Log.d(Constant.TAG,"--MyContentProvider--update--");        int matchId= sURIMatcher.match(uri);        switch(matchId) {            case MyStudyTableFlag:                SQLiteDatabase db1 = SqlManager.getInstance(getContext()).getWritableDatabase();                db1.update(Constant.TableName, values, selection, selectionArgs);                break;            case MyStudyTableNumFlag:                long id = ContentUris.parseId(uri);//ContentUris的使用。                Log.d(Constant.TAG, "ContentUris:" + id);                SQLiteDatabase db2 = SqlManager.getInstance(getContext()).getWritableDatabase();                String[] args = {"" + id};                db2.update(Constant.TableName, values,"_id = ?", args);                break;        }        return 0;    }}

第二步注册contentProvider:

  <provider            android:name=".contentprovider.MyContentProvider"  android:authorities="com.czh.mycontentprovider.study" />

第三步外部应用增删改查
查找数据的代码:

String uriStr = "content://com.czh.mycontentprovider.study/"+Constant.TableName+"/4";String[] projection = {Constant._ID,Constant.NAME,Constant.AGE};String selection = "_id = ?";String[] selectionArgs = {"2"};Cursor cursor =  getContentResolver().query(Uri.parse(uriStr),projection,selection,selectionArgs,null,null);CursorAdapter ca = new SqlAdapter(MainActivity.this, cursor);listView.setAdapter(ca);

插入数据的代码:

   String uriStr = "content://com.czh.mycontentprovider.study";        ContentValues cv = new ContentValues();        cv.put("name","yyd插入测试");        cv.put("age",100);        getContentResolver().insert(Uri.parse(uriStr), cv);

删除数据的代码:

 String uriStr = "content://com.czh.mycontentprovider.study/"+Constant.TableName+"/10";        // Uri url,  String where,  String[] selectionArgs        getContentResolver().delete(Uri.parse(uriStr),null,null);

更新数据的代码:

 String uriStr = "content://com.czh.mycontentprovider.study/"+Constant.TableName+"/10";getContentResolver().delete(Uri.parse(uriStr),null,null);

4 contentprovider使用小结

外部应用操作步骤:

第一步获取ContentResolver

ContentResolver cr = getContentResolver();

第二步:

利用ContentResolver,结合uri进行增删改查。

主应用使用步骤:

第一步:继承contentprovider,重写它的方法
第二步:在注册contentprovider。

监听ContentProvider中数据的变化

步骤:
1.在ContentProvider类的insert\update\delete方法添加改变通知:

this.getContext().getContentResolver().notifyChange(URI,null);
参数 介绍 uri 表示监听的URI null 表示发送消息给任何人;

2.在外部应用设置监听:

getContentResolver().registerContentObserver(Uri.parse("content://com.czh.mycontentprovider.study"),true,new PersonObserver(new Handler())); private class PersonObserver extends ContentObserver{//监听        public PersonObserver(Handler handler) {            super(handler);        }        //当ContentProvier数据发生改变,则触发该函数        @Override        public void onChange(boolean selfChange) {            super.onChange(selfChange);            Log.i("myapp", "数据改变");        }    }

挺简单的,这里不过多讨论。

5 ContentProvider深入理解

讲到最后了,我们来讨论几个问题:

1. 在应用程序A里面怎么跨进程拿到ContentProvider的对象呢?2. ContentProvider实例对象是保存在哪里呢?3. ContentProvider的方法实现要注意线程安全吗?

这块还没研究透彻,后面补充。

6 参考文档

【1】Android开发手册
【2】内容提供程序
【3】ContentProvider浅析—写点你平时没注意到的

7 结尾

文章的结尾奉送上代码方便大家对照学习。
好了就讲到这里吧。希望对大家有所帮助!如果觉的我写的不错,就点个赞吧!

0 0