转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103 ),谢谢支持!
前言
学习完了多种数据存储方法,接下来我们来学习,如果进程间共享数据
ContentProvider内容提供者
ContentProvider 进程间通讯,进程间数据的访问 / 对外共享数据用
优点:提供了统一的访问方式
原理分析图:
实现抽象类ContentProvider
Android应用实现抽象类ContentProvider ,并实现对本地数据库增删查改的四个方法,且在清单文件注册该 ContentProvider 。就如同给其他应用提供了一个读取本地数据的接口。
public class PersonProvider extends ContentProvider {private DBhelp dbOpenHelper;//UriMatcher类//addURI(authority, path, code);注册Uri,指定code//match(uri);匹配Uri,如果有返回code,无返回-1.//UriMatcher 其实是一个arrayList集合 private static final UriMatcher MATCHER = new UriMatcher( UriMatcher.NO_MATCH);// 判断Uri private static final int PERSONS = 1; private static final int PERSON = 2; static { // (主机名,路径部分,匹配码); // 添加进了要等待的匹配的路径 MATCHER.addURI("com.cym.provides.PersonProvider", "person", PERSONS); MATCHER.addURI("com.cym.provides.PersonProvider", "person/#", PERSON); // # // 代表数字 // * // 代表所有字符 }@Override public boolean onCreate() {// 这个方法不是由程序员调用的而是由系统调用的,当这个程序启动被创建出来后这个方法自动调用,所以它只会被调用一次,适于做数据初始化操作。 ContentProvider在启动应用程序的时候创建。防止并发状态,当android应用程序安装的时候,会在注册表里面把应用程序所有的信息进行注册。所有在开机之后,系统里面所有的contentProvider都会被创建出来 dbOpenHelper = new DBhelp(getContext()); return false; } // 我们还需要判断URI 这样就能够知道外部应用操作的处理方式 // 以下方法都是提供给外部应用操作query,insert,delete,update//Uri 与 ContentUri//ContentUris.parseId(uri);得到当前uri的资源id://Uri.parse(“conten://authority/path”);将字符串解析成Uri对象// ContentUris.withAppendedId(uri, id);给当前uri添加id,并且返回新的uri: @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); Cursor cursor = null; switch (MATCHER.match(uri)) { case PERSONS: cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder); break; case PERSON://得到当前uri的资源id: long rowid = ContentUris.parseId(uri); String where = "_id=" + rowid; if (selection != null && !"".equals(selection.trim())) { where += " and " + selection; } cursor = db.query("person", projection, where, selectionArgs, null, null, sortOrder); break; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } return cursor; } // 返回操作的内容文件 @Override public String getType(Uri uri) { switch (MATCHER.match(uri)) { case PERSONS: return "vnd.android.cursor.dir/person"; case PERSON: return "vnd.android.cursor.item/person"; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case PERSONS: long rowid = db.insert("person", "name", values);// 等于你的主键值 // content://com.cym.provides.personprovider/person/10 // Uri insertUri = // Uri.parse("content://com.cym.provides.personprovider/person/"+rowid); // 两种方法 Uri insertUri = ContentUris.withAppendedId(uri, rowid); //通过上下文拿到contentResolver Context context = getContext(); ContentResolver cr = context.getContentResolver(); //调用该方法时 通知监听 cr.notifyChange(uri, null); return insertUri; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { } @Override public int update(Uri uri, ContentValues values, String selection, }}
清单文件注册
安卓应用提供给其他应用操作本地数据的接口,必须在清单文件注册。
在application标签内部添加provider标签
<providerandroid:name=".PersonProvider"android:authorities="com.cym.provides.PersonProvider"/>
Name参数表示 Provider的位置。. 代表在此包底下
Authorities参数表示域名类似于www.hao123.com (必须唯一:本地唯一)
虽然没有规定Authorities的书写规范,但是我们一般使用PersonProvider的全名
例:com.cym.provides.PersonProvider
其他应用访问
为什么使用uri
保证数据的安全性不让所有的数据对外暴露,所以我们可以指定只有那些uri 可以被被人访问,所以 db 中放入一组 uri 集合
其他应用通过Context.getContentResolver() 得到 ContentResolver 对象,根据指定的 Uri 可以调用 ContentProvider 对应的增删查改方法对数据库进行操作。
客户端的ContentResolver 对象要想调用服务端 ContentProvider 对象的响应的方法,也必须通过该 ContentProvider 对应的 Uri 找到 ContentProvider ,才能实现调用。
Uri代表了要操作的数据, Uri 主要包含了两部分信息: 1 》需要操作的 ContentProvider , 2 》对 ContentProvider 中的什么数据进行操作,一个 Uri 由以下几部分组成:
ContentProvider(内容提供者)的 scheme 已经由Android 所规定, scheme 为: content://
主机名(或叫Authority )用于唯一标识这个 ContentProvider ,外部调用者可以根据这个标识来找到它。
路径(path )可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下 :
要操作person 表中 id 为 10 的记录,可以构建这样的路径 :/person/10
要操作person 表中 id 为 10 的记录的 name 字段, person/10/name
要操作person 表中的所有记录,可以构建这样的路径 :/person
要操作xxx 表中的记录,可以构建这样的路径 :/xxx
当然要操作的数据不一定来自数据库,也可以是文件、xml 或网络等其他存储方式,如下 :
要操作xml 文件中 person 节点下的 name 节点,可以构建这样的路径: /person/name
如果要把一个字符串转换成Uri ,可以使用 Uri 类中的 parse() 方法,如下:
Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")//生成指向Server端CP的Uri实例对象//得到域名 uri=Uri.parse("content://com.huaao.ContentProvider");//拿到resovlerContentResolver cr= this.getContext().getContentResolver(); //调用ContentResolver对象的query()方法。传入域名 cr.query(uri, null, null, null, null);//客户端代码.delete.setOnClickListener(new OnClickListener() { public void onClick(View v) { //指定资源id uri=Uri.parse("content://com.huaao.ContentProvider/student/2"); //调用CR的delete()方法 cr.delete(url, null, null); }});
监听数据更改
ContentObserver
安卓提供ContentObserver 实现数据变化后监听
监听三步骤:
1,实现抽象类 ContentObserver ,重写 onChange() 方法
2,调用 ContentProvider 注册 ContentObserver 。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ContentResolver cr = getContentResolver();
Uri uri = Uri.parse("content://com.cym.provides.PersonProvider/person");
Cursor query = cr.query(uri, null, null, null, null);
while (query.moveToNext()) {
Log.i(TAG, query.getString(query.getColumnIndex("name")));
}
Log.i(TAG, "第一次数据查询完了");
ContentObserver observer = new MyContentObserver(new Handler());
//注册监听
cr.registerContentObserver(uri, true, observer);
}
//实现抽象类 ContentObserver,
private class MyContentObserver extends ContentObserver{
public MyContentObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
@Override
//重写onchenge
public void onChange(boolean selfChange) {
super.onChange(selfChange);
ContentResolver cr = getContentResolver();
Uri uri = Uri.parse("content://com.cym.provides.PersonProvider/person");
Cursor query = cr.query(uri, null, null, null, null);
while (query.moveToNext()) {
Log.i(TAG, query.getString(query.getColumnIndex("name")));
}
Log.i(TAG, "第二次次数据查询完了");
}
}
registerContentObserver(uri,是否连带子Uri,Observer对象);
3 , 当数据发生变化,提醒监听者
notifyChange(uri, Observer);
在你要监听的方法里写入notifyChange(uri, Observer);
例子:
public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case PERSONS: long rowid = db.insert("person", "name", values);// 等于你的主键值 // content://com.cym.provides.personprovider/person/10 // Uri insertUri = // Uri.parse("content://com.cym.provides.personprovider/person/"+rowid); // 两种方法 Uri insertUri = ContentUris.withAppendedId(uri, rowid); //通过上下文拿到contentResolver Context context = getContext(); ContentResolver cr = context.getContentResolver(); //调用该方法时 通知监听 cr.notifyChange(uri, null); return insertUri; default: throw new IllegalArgumentException("this is Unknown Uri:" + uri); } }
最后结果流程就是 第一个次查询数据完成之后,我们在给查询这个应用注册了监听并且重写onChange() 方法 ,以及我们在insert方法里面写了通知监听如果有其他应用访问 insert 方法,就会触发监听同志,就会执行注册监听时的 onChange()方法。