今天我们来讲解一下如何创建及调用自己的ContentProvIDer。
在前面两篇文章中我们分别讲了如何读写联系人和短消息,相信大家对于ContentProvIDer的 *** 作方法已经有了一定程度的了解。在有些场合,除了 *** 作ContentProvIDer之外,我们还有可能需要创建自己的ContentProvIDer,来提供信息共享的服务,这就要求我们很好的掌握ContentProvIDer的创建及使用技巧。下面我们就由表及里的逐步讲解每个步骤。
在正式开始实例演示之前,我们先来了解以下两个知识点:
授权:
在AndroID中,每一个ContentProvIDer都会用类似于域名的字符串来注册自己,我们成为授权(authority)。这个唯一标识的字符串是此ContentProvIDer可提供的一组URI的基础,有了这个基础,才能够向外界提供信息的共享服务。
授权是在AndroIDManifest.xml中完成的,每一个ContentProvIDer必须在此声明并授权,方式如下:
<provIDer androID:name=".someProvIDer" androID:authoritIEs="com.your-company.someProvIDer"/>
上面的<provIDer>元素指明了ContentProvIDer的提供者是“SomeProvIDer”这个类,并为其授权,授权的基础URI为“com.your-company.someProvIDer”。有了这个授权信息,系统可以准确的定位到具体的ContentProvIDer,从而使访问者能够获取到指定的信息。这和浏览Web页面的方式很相似,“SomeProvIDer”就像一台具体的服务器,而“com.your-company.someProvIDer”就像注册的域名,相信大家对这个概念并不陌生,由此联想一下就可以了解ContentProvIDer授权的作用了。(需要注意的是,除了AndroID内置应用程序之外,第三方程序应尽量使用以上方式的完全限定的授权名。)
MIME类型:
就像网站返回给定URL的MIME(Multipurpose Internet Mail Extensions,多用途Internet邮件扩展)类型一样(这使浏览器能够用正确的程序来查看内容),ContentProvIDer还负责返回给定URI的MIME类型。根据MIME类型规范,MIME类型包含两部分:类型和子类型。例如:text/HTML,text/CSS,text/xml等等。
AndroID也遵循类似的约定来定义MIME类型。
对于单条记录,MIME类型类似于:
vnd.androID.cursor.item/vnd.your-company.content-type
而对于记录的集合,MIME类型类似于:
vnd.androID.cursor.dir/vnd.your-company.comtent-type
其中的vnd表示这些类型和子类型具有非标准的、供应商特定的形式;content-type可以根据ContentProvIDer的功能来定,比如日记的ContentProvIDer可以为note,日程安排的ContentProvIDer可以为schedule,等等。
了解了以上两个知识点之后,我们就结合实例来演示一下具体的过程。
我们将会创建一个记录person信息的ContentProvIDer,实现对person的CRUD *** 作,访问者可以通过下面路径 *** 作我们的ContentProvIDer:
访问者可以通过“[BASE_URI]/persons”来 *** 作person集合,也可以通过“[BASE_URI]/persons/#”的形式 *** 作单个person。
我们创建一个person的ContentProvIDer需要两个步骤:
1.创建PersonProvIDer类:
我们需要继承ContentProvIDer类,实现onCreate、query、insert、update、delete和getType这几个方法。具体代码如下:
package com.scott.provIDer; import androID.content.ContentProvIDer; import androID.content.ContentUris; import androID.content.ContentValues; import androID.content.UriMatcher; import androID.database.Cursor; import androID.database.sqlite.sqliteDatabase; import androID.net.Uri; public class PersonProvIDer extends ContentProvIDer { private static final UriMatcher matcher; private DBHelper helper; private sqliteDatabase db; private static final String AUTHORITY = "com.scott.provIDer.PersonProvIDer"; private static final int PERSON_ALL = 0; private static final int PERSON_ONE = 1; public static final String CONTENT_TYPE = "vnd.androID.cursor.dir/vnd.scott.person"; public static final String CONTENT_ITEM_TYPE = "vnd.androID.cursor.item/vnd.scott.person"; //数据改变后立即重新查询 private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/persons"); static { matcher = new UriMatcher(UriMatcher.NO_MATCH); matcher.addURI(AUTHORITY,"persons",PERSON_ALL); //匹配记录集合 matcher.addURI(AUTHORITY,"persons/#",PERSON_ONE); //匹配单条记录 } @OverrIDe public boolean onCreate() { helper = new DBHelper(getContext()); return true; } @OverrIDe public String getType(Uri uri) { int match = matcher.match(uri); switch (match) { case PERSON_ALL: return CONTENT_TYPE; case PERSON_ONE: return CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("UnkNown URI: " + uri); } } @OverrIDe public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder) { db = helper.getReadableDatabase(); int match = matcher.match(uri); switch (match) { case PERSON_ALL: //doesn't need any code in my provIDer. break; case PERSON_ONE: long _ID = ContentUris.parseID(uri); selection = "_ID = ?"; selectionArgs = new String[]{String.valueOf(_ID)}; break; default: throw new IllegalArgumentException("UnkNown URI: " + uri); } return db.query("person",projection,selection,selectionArgs,null,sortOrder); } @OverrIDe public Uri insert(Uri uri,ContentValues values) { int match = matcher.match(uri); if (match != PERSON_ALL) { throw new IllegalArgumentException("Wrong URI: " + uri); } db = helper.getWritableDatabase(); if (values == null) { values = new ContentValues(); values.put("name","no name"); values.put("age","1"); values.put("info","no info."); } long rowID = db.insert("person",values); if (rowID > 0) { notifyDataChanged(); return ContentUris.withAppendedID(uri,rowID); } return null; } @OverrIDe public int delete(Uri uri,String[] selectionArgs) { db = helper.getWritableDatabase(); int match = matcher.match(uri); switch (match) { case PERSON_ALL: //doesn't need any code in my provIDer. break; case PERSON_ONE: long _ID = ContentUris.parseID(uri); selection = "_ID = ?"; selectionArgs = new String[]{String.valueOf(_ID)}; } int count = db.delete("person",selectionArgs); if (count > 0) { notifyDataChanged(); } return count; } @OverrIDe public int update(Uri uri,ContentValues values,String[] selectionArgs) { db = helper.getWritableDatabase(); int match = matcher.match(uri); switch (match) { case PERSON_ALL: //doesn't need any code in my provIDer. break; case PERSON_ONE: long _ID = ContentUris.parseID(uri); selection = "_ID = ?"; selectionArgs = new String[]{String.valueOf(_ID)}; break; default: throw new IllegalArgumentException("UnkNown URI: " + uri); } int count = db.update("person",values,selectionArgs); if (count > 0) { notifyDataChanged(); } return count; } //通知指定URI数据已改变 private voID notifyDataChanged() { getContext().getContentResolver().notifyChange(NOTIFY_URI,null); } }
在PersonProvIDer中,我们定义了授权地址为“com.scott.provIDer.PersonProvIDer”,相信大家在前面也有所了解了。基于这个授权,我们使用了一个UriMatcher对其路径进行匹配,“[BASE_URI]/persons"和“[BASE_URI]/persons/#”这两种路径我们在上面也介绍过,分别对应记录集合和单个记录的 *** 作。在query、insert、update和delete方法中我们根据UriMatcher匹配结果来判断该URI是 *** 作记录集合还是单条记录,从而采取不同的处理方法。在getType方法中,我们会根据匹配的结果返回不同的MIME类型,这一步是不能缺少的,比如我们在query方法中有可能是查询全部集合,有可能是查询单条记录,那么我们返回的Cursor或是集合类型,或是单条记录,这个跟getType返回的MIME类型是一致的,就好像浏览网页一样,指定的url返回的信息是什么类型,那么浏览器就应该接收到对应的MIME类型。另外,我们注意到,上面代码中,在insert、update、delete方法中都调用了notifyDataChanged方法,这个方法中仅有的一步 *** 作就是通知“[BASE_URI]/persons"的访问者,数据发生改变了,应该重新加载了。
在我们的PersonProvIDer中,我们用到了Person、DBHelper类,代码如下:
package com.scott.provIDer; public class Person { public int _ID; public String name; public int age; public String info; public Person() { } public Person(String name,int age,String info) { this.name = name; this.age = age; this.info = info; } } [java] vIEw plain copypackage com.scott.provIDer; import androID.content.Context; import androID.database.sqlite.sqliteDatabase; import androID.database.sqlite.sqliteOpenHelper; public class DBHelper extends sqliteOpenHelper { private static final String DATABASE_name = "provIDer.db"; private static final int DATABASE_VERSION = 1; public DBHelper(Context context) { super(context,DATABASE_name,DATABASE_VERSION); } @OverrIDe public voID onCreate(sqliteDatabase db) { String sql = "CREATE table IF NOT EXISTS person" + "(_ID INTEGER PRIMARY KEY autoINCREMENT,name VARCHAR,age INTEGER,info TEXT)"; db.execsql(sql); } @OverrIDe public voID onUpgrade(sqliteDatabase db,int oldVersion,int newVersion) { db.execsql("DROP table IF EXISTS person"); onCreate(db); } }
最后,要想让这个ContentProvIDer生效,我们需要在AndroIDManifest.xml中声明并为其授权,如下所示:
<provIDer androID:name=".PersonProvIDer" androID:authoritIEs="com.scott.provIDer.PersonProvIDer" androID:multiprocess="true"/>
其中,androID:multiprocess代表是否允许多进程 *** 作。另外我们也可以为其声明相应的权限,对应的属性是:androID:permission。
2.调用PersonProvIDer类:
完成了person的ContentProvIDer后,下面我们来看一下如何访问它。这一步我们在MainActivity中完成,看下面代码:
package com.scott.provIDer; import java.util.ArrayList; import androID.app.Activity; import androID.content.ContentResolver; import androID.content.ContentUris; import androID.content.ContentValues; import androID.database.Cursor; import androID.database.CursorWrapper; import androID.net.Uri; import androID.os.Bundle; import androID.os.Handler; import androID.os.Message; import androID.vIEw.VIEw; import androID.Widget.ListVIEw; import androID.Widget.SimpleCursorAdapter; public class MainActivity extends Activity { private ContentResolver resolver; private ListVIEw ListVIEw; private static final String AUTHORITY = "com.scott.provIDer.PersonProvIDer"; private static final Uri PERSON_ALL_URI = Uri.parse("content://" + AUTHORITY + "/persons"); private Handler handler = new Handler() { public voID handleMessage(Message msg) { //update records. requery(); }; }; @OverrIDe public voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.main); resolver = getContentResolver(); ListVIEw = (ListVIEw) findVIEwByID(R.ID.ListVIEw); //为PERSON_ALL_URI注册变化通知 getContentResolver().registerContentObserver(PERSON_ALL_URI,true,new PersonObserver(handler)); } /** * 初始化 * @param vIEw */ public voID init(VIEw vIEw) { ArrayList<Person> persons = new ArrayList<Person>(); Person person1 = new Person("Ella",22,"lively girl"); Person person2 = new Person("Jenny","beautiful girl"); Person person3 = new Person("Jessica",23,"sexy girl"); Person person4 = new Person("Kelly","hot baby"); Person person5 = new Person("Jane",25,"pretty woman"); persons.add(person1); persons.add(person2); persons.add(person3); persons.add(person4); persons.add(person5); for (Person person : persons) { ContentValues values = new ContentValues(); values.put("name",person.name); values.put("age",person.age); values.put("info",person.info); resolver.insert(PERSON_ALL_URI,values); } } /** * 查询所有记录 * @param vIEw */ public voID query(VIEw vIEw) { // Uri personOneUri = ContentUris.withAppendedID(PERSON_ALL_URI,1);查询_ID为1的记录 Cursor c = resolver.query(PERSON_ALL_URI,null); CursorWrapper cursorWrapper = new CursorWrapper(c) { @OverrIDe public String getString(int columnIndex) { //将简介前加上年龄 if (getColumnname(columnIndex).equals("info")) { int age = getInt(getColumnIndex("age")); return age + " years old," + super.getString(columnIndex); } return super.getString(columnIndex); } }; //Cursor须含有"_ID"字段 SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,androID.R.layout.simple_List_item_2,cursorWrapper,new String[]{"name","info"},new int[]{androID.R.ID.text1,androID.R.ID.text2}); ListVIEw.setAdapter(adapter); startManagingCursor(cursorWrapper); //管理Cursor } /** * 插入一条记录 * @param vIEw */ public voID insert(VIEw vIEw) { Person person = new Person("Alina",26,"attractive lady"); ContentValues values = new ContentValues(); values.put("name",person.name); values.put("age",person.age); values.put("info",person.info); resolver.insert(PERSON_ALL_URI,values); } /** * 更新一条记录 * @param vIEw */ public voID update(VIEw vIEw) { Person person = new Person(); person.name = "Jane"; person.age = 30; //将指定name的记录age字段更新为30 ContentValues values = new ContentValues(); values.put("age",person.age); resolver.update(PERSON_ALL_URI,"name = ?",new String[]{person.name}); //将_ID为1的age更新为30 // Uri updateUri = ContentUris.withAppendedID(PERSON_ALL_URI,1); // resolver.update(updateUri,null); } /** * 删除一条记录 * @param vIEw */ public voID delete(VIEw vIEw) { //删除_ID为1的记录 Uri delUri = ContentUris.withAppendedID(PERSON_ALL_URI,1); resolver.delete(delUri,null); //删除所有记录 // resolver.delete(PERSON_ALL_URI,null); } /** * 重新查询 */ private voID requery() { //实际 *** 作中可以查询集合信息后Adapter.notifyDataSetChanged(); query(null); } }
我们看到,在上面的代码中,分别对应每一种情况进行测试,相对较为简单。我们主要讲一下registerContentObserver这一环节。
在前面的PersonProvIDer我们也提到,在数据更改后,会向指定的URI访问者发出通知,以便于更新查询记录。大家注意,仅仅是ContentProvIDer出力还不够,我们还需要在访问者中注册一个ContentObserver,才能够接收到这个通知。下面我们创建一个
PersonObserver:package com.scott.provIDer; import androID.database.ContentObserver; import androID.os.Handler; import androID.os.Message; import androID.util.Log; public class PersonObserver extends ContentObserver { public static final String TAG = "PersonObserver"; private Handler handler; public PersonObserver(Handler handler) { super(handler); this.handler = handler; } @OverrIDe public voID onChange(boolean selfChange) { super.onChange(selfChange); Log.i(TAG,"data changed,try to requery."); //向handler发送消息,更新查询记录 Message msg = new Message(); handler.sendMessage(msg); } }
这样一来,当ContentProvIDer发来通知之后,我们就能立即接收到,从而向handler发送一条消息,重新查询记录,使我们能够看到最新的记录信息。
最后,我们要在AndroIDManifest.xml中为MainActivity添加MIME类型过滤器,告诉系统MainActivity可以处理的信息类型:
<!-- MIME类型 --> <intent-filter> <data androID:mimeType="vnd.androID.cursor.dir/vnd.scott.person"/> </intent-filter> <intent-filter> <data androID:mimeType="vnd.androID.cursor.item/vnd.scott.person"/> </intent-filter>
这样就完成了访问者的代码,我们来看一下效果:
鉴于 *** 作类型太多,我在这里就不再展示了,大家可以自己试一试。
原文链接:http://blog.csdn.net/liuhe688/article/details/7050868
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
总结以上是内存溢出为你收集整理的android基础总结篇之八:创建及调用自己的ContentProvider全部内容,希望文章能够帮你解决android基础总结篇之八:创建及调用自己的ContentProvider所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)