Android 中自定义ContentProvider与ContentObserver的使用简单实例
程序员文章站
2023-12-18 22:30:22
android 中自定义contentprovider与contentobserver的使用简单实例
示例说明:
该示例中一共包含两个工程。其中一个工程完成了自定义...
android 中自定义contentprovider与contentobserver的使用简单实例
示例说明:
该示例中一共包含两个工程。其中一个工程完成了自定义contentprovider,另外一个工程用于测试该自定义contentprovider且在该工程中使用了contentobserver监听自定义contentprovider的数据变化
以下代码为工程testcontentprovider
contentprovidertest如下:
package cn.testcontentprovider; 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; /** * demo描述: * 自定义contentprovider的实现 * contentprovider主要用于在不同的应用程序之间共享数据,这也是官方推荐的方式. * * 注意事项: * 1 在androidmanifest.xml中注册contentprovider时的属性 * android:exported="true"表示允许其他应用访问. * 2 注意*和#这两个符号在uri中的作用 * 其中*表示匹配任意长度的字符 * 其中#表示匹配任意长度的数据 * 所以: * 一个能匹配所有表的uri可以写成: * content://cn.bs.testcontentprovider/* * 一个能匹配person表中任意一行的uri可以写成: * content://cn.bs.testcontentprovider/person/# * */ public class contentprovidertest extends contentprovider { private sqlitedatabaseopenhelper msqlitedatabaseopenhelper; private final static string authority="cn.bs.testcontentprovider"; private static urimatcher murimatcher; private static final int person_dir = 0; private static final int person = 1; /** * 利用静态代码块初始化urimatcher * 在urimatcher中包含了多个uri,每个uri代表一种操作 * 当调用urimatcher.match(uri uri)方法时就会返回该uri对应的code; * 比如此处的persons和person */ static { murimatcher = new urimatcher(urimatcher.no_match); // 该uri表示返回所有的person,其中persons为该特定uri的标识码 murimatcher.adduri(authority, "person", person_dir); // 该uri表示返回某一个person,其中person为该特定uri的标识码 murimatcher.adduri(authority, "person/#", person); } /** * 在自定义contentprovider中必须覆写gettype(uri uri)方法. * 该方法用于获取uri对象所对应的mime类型. * * 一个uri对应的mime字符串遵守以下三点: * 1 必须以vnd开头 * 2 如果该uri对应的数据可能包含多条记录,那么返回字符串应该以"vnd.android.cursor.dir/"开头 * 3 如果该uri对应的数据只包含一条记录,那么返回字符串应该以"vnd.android.cursor.item/"开头 */ @override public string gettype(uri uri) { switch (murimatcher.match(uri)) { case person_dir: return "vnd.android.cursor.dir/"+authority+".persons"; case person: return "vnd.android.cursor.item/"+authority+".person"; default: throw new illegalargumentexception("unknown uri"+uri.tostring()); } } @override public boolean oncreate() { msqlitedatabaseopenhelper=new sqlitedatabaseopenhelper(getcontext()); return true; } /** * 插入操作: * 插入操作只有一种可能:向一张表中插入 * 返回结果为新增记录对应的uri * 方法db.insert()返回结果为新增记录对应的主键值 */ @override public uri insert(uri uri, contentvalues values) { sqlitedatabase db = msqlitedatabaseopenhelper.getwritabledatabase(); switch (murimatcher.match(uri)) { case person_dir: long newid = db.insert("person", "name,phone,salary", values); //向外界通知该contentprovider里的数据发生了变化 ,以便contentobserver作出相应 getcontext().getcontentresolver().notifychange(uri, null); return contenturis.withappendedid(uri, newid); default: throw new illegalargumentexception("unknown uri" + uri.tostring()); } } /** * 更新操作: * 更新操作有两种可能:更新一张表或者更新某条数据 * 在更新某条数据时原理类似于查询某条数据,见下. */ @override public int update(uri uri, contentvalues values, string selection,string[] selectionargs) { sqlitedatabase db = msqlitedatabaseopenhelper.getwritabledatabase(); int updatednum = 0; switch (murimatcher.match(uri)) { // 更新表 case person_dir: updatednum = db.update("person", values, selection, selectionargs); break; // 按照id更新某条数据 case person: long id = contenturis.parseid(uri); string where = "personid=" + id; if (selection != null && !"".equals(selection.trim())) { where = selection + " and " + where; } updatednum = db.update("person", values, where, selectionargs); break; default: throw new illegalargumentexception("unknown uri" + uri.tostring()); } //向外界通知该contentprovider里的数据发生了变化 ,以便contentobserver作出相应 getcontext().getcontentresolver().notifychange(uri, null); return updatednum; } /** * 删除操作: * 删除操作有两种可能:删除一张表或者删除某条数据 * 在删除某条数据时原理类似于查询某条数据,见下. */ @override public int delete(uri uri, string selection, string[] selectionargs) { sqlitedatabase db = msqlitedatabaseopenhelper.getwritabledatabase(); int deletednum = 0; switch (murimatcher.match(uri)) { // 删除表 case person_dir: deletednum = db.delete("person", selection, selectionargs); break; // 按照id删除某条数据 case person: long id = contenturis.parseid(uri); string where = "personid=" + id; if (selection != null && !"".equals(selection.trim())) { where = selection + " and " + where; } deletednum = db.delete("person", where, selectionargs); break; default: throw new illegalargumentexception("unknown uri" + uri.tostring()); } //向外界通知该contentprovider里的数据发生了变化 ,以便contentobserver作出相应 getcontext().getcontentresolver().notifychange(uri, null); return deletednum; } /** * 查询操作: * 查询操作有两种可能:查询一张表或者查询某条数据 * * 注意事项: * 在查询某条数据时要注意--因为此处是按照personid来查询 * 某条数据,但是同时可能还有其他限制.例如: * 要求personid为2且name为xiaoming1 * 所以在查询时分为两步: * 第一步: * 解析出personid放入where查询条件 * 第二步: * 判断是否有其他限制(如name),若有则将其组拼到where查询条件. * * 详细代码见下. */ @override public cursor query(uri uri, string[] projection, string selection,string[] selectionargs, string sortorder) { sqlitedatabase db = msqlitedatabaseopenhelper.getwritabledatabase(); cursor cursor =null; switch (murimatcher.match(uri)) { // 查询表 case person_dir: cursor = db.query("person", projection, selection, selectionargs,null, null, sortorder); break; // 按照id查询某条数据 case person: // 第一步: long id = contenturis.parseid(uri); string where = "personid=" + id; // 第二步: if (selection != null && !"".equals(selection.trim())) { where = selection + " and " + where; } cursor = db.query("person", projection, where, selectionargs, null, null, sortorder); break; default: throw new illegalargumentexception("unknown uri" + uri.tostring()); } return cursor; } }
sqlitedatabaseopenhelper如下:
package cn.testcontentprovider; import android.content.context; import android.database.sqlite.sqlitedatabase; import android.database.sqlite.sqliteopenhelper; public class sqlitedatabaseopenhelper extends sqliteopenhelper { public sqlitedatabaseopenhelper(context context) { super(context, "contentprovidertest.db", null, 1); } @override public void oncreate(sqlitedatabase db) { db.execsql("create table person(personid integer primary key autoincrement,name varchar(20),phone varchar(12),salary integer(12))"); } //当数据库版本号发生变化时调用该方法 @override public void onupgrade(sqlitedatabase db, int arg1, int arg2) { //db.execsql("alter table person add phone varchar(12) null"); //db.execsql("alter table person add salary integer null"); } } mainactivity如下: [java] view plain copy package cn.testcontentprovider; import android.app.activity; import android.os.bundle; public class mainactivity extends activity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); } }
androidmanifest.xml如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.testcontentprovider" android:versioncode="1" android:versionname="1.0" > <uses-sdk android:minsdkversion="8" android:targetsdkversion="8" /> <uses-permission android:name="android.permission.internet" /> <uses-permission android:name="android.permission.access_network_state" /> <uses-permission android:name="android.permission.write_external_storage" /> <uses-permission android:name="android.permission.mount_unmount_filesystems" /> <application android:allowbackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/apptheme" > <activity android:name="cn.testcontentprovider.mainactivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter> </activity> <provider android:name="cn.testcontentprovider.contentprovidertest" android:authorities="cn.bs.testcontentprovider" android:exported="true" /> </application> </manifest>
main.xml如下:
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="该应用包含一个自定义的contentprovider" android:textsize="15sp" android:layout_centerinparent="true" /> </relativelayout>
以下代码为工程testbaidu
mainactivity如下:
package cn.testbaidu; import android.net.uri; import android.os.bundle; import android.os.handler; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.app.activity; import android.content.contentresolver; import android.content.contentvalues; import android.database.contentobserver; import android.database.cursor; /** * demo描述: * 应用a(testbaidu)调用另外一个应用(testcontentprovider)中的自定义contentprovider,即: * 1 自定义contentprovider的使用 * 2 其它应用调用该contentprovider * 3 contentobserver的使用 * * 备注说明: * 1 该例子在以前版本的基础上整理了代码 * 2 该例子在以前版本的基础上融合了contentobserver的使用 * 利用contentobserver随时监听contentprovider的数据变化. * 为实现该功能需要在自定义的contentprovider的insert(),update(),delete() * 方法中调用getcontext().getcontentresolver().notifychange(uri, null); * 向外界通知该contentprovider里的数据发生了变化 ,以便contentobserver作出相应 * * 测试方法: * 1 依次测试contentprovider的增查删改(注意该顺序)!! * 2 其它应用查询该contentprovider的数据 * */ public class mainactivity extends activity { private button maddbutton; private button mdeletebutton; private button mupdatebutton; private button mquerybutton; private button mtypebutton; private long lasttime=0; private contentresolver mcontentresolver; private contentobserversubclass mcontentobserversubclass; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); init(); initcontentobserver(); } private void init() { mcontentresolver=this.getcontentresolver(); maddbutton=(button) findviewbyid(r.id.addbutton); maddbutton.setonclicklistener(new clicklistenerimpl()); mdeletebutton=(button) findviewbyid(r.id.deletebutton); mdeletebutton.setonclicklistener(new clicklistenerimpl()); mupdatebutton=(button) findviewbyid(r.id.updatebutton); mupdatebutton.setonclicklistener(new clicklistenerimpl()); mquerybutton=(button) findviewbyid(r.id.querybutton); mquerybutton.setonclicklistener(new clicklistenerimpl()); mtypebutton=(button) findviewbyid(r.id.typebutton); mtypebutton.setonclicklistener(new clicklistenerimpl()); } // 注册一个针对contentprovider的contentobserver用来观察内容提供者的数据变化 private void initcontentobserver() { uri uri = uri.parse("content://cn.bs.testcontentprovider/person"); mcontentobserversubclass=new contentobserversubclass(new handler()); this.getcontentresolver().registercontentobserver(uri, true,mcontentobserversubclass); } @override protected void ondestroy() { super.ondestroy(); if (mcontentobserversubclass!=null) { this.getcontentresolver().unregistercontentobserver(mcontentobserversubclass); } } // 自定义一个内容观察者contentobserver private class contentobserversubclass extends contentobserver { public contentobserversubclass(handler handler) { super(handler); } //采用时间戳避免多次调用onchange( ) @override public void onchange(boolean selfchange) { super.onchange(selfchange); system.out.println("contentobserver onchange() selfchange="+ selfchange); if (system.currenttimemillis()-lasttime>2000) { contentresolver resolver = getcontentresolver(); uri uri = uri.parse("content://cn.bs.testcontentprovider/person"); // 获取最新的一条数据 cursor cursor = resolver.query(uri, null, null, null,"personid desc limit 1"); while (cursor.movetonext()) { int personid = cursor.getint(cursor.getcolumnindex("personid")); system.out.println("内容提供者中的数据发生变化,现数据中第一条数据的personid="+ personid); } cursor.close(); lasttime=system.currenttimemillis(); }else{ system.out.println("时间间隔过短,忽略此次更新"); } } @override public boolean deliverselfnotifications() { return true; } } private class clicklistenerimpl implements onclicklistener { @override public void onclick(view v) { switch (v.getid()) { case r.id.addbutton: person person = null; for (int i = 0; i < 5; i++) { person = new person("xiaoming" + i, "9527" + i, (8888 + i)); testinsert(person); } break; case r.id.deletebutton: testdelete(1); break; case r.id.updatebutton: testupdate(3); break; case r.id.querybutton: // 查询表 // queryfromcontentprovider(-1); // 查询personid=2的数据 testquery(2); break; case r.id.typebutton: testtype(); break; default: break; } } } private void testinsert(person person) { contentvalues contentvalues=new contentvalues(); contentvalues.put("name", person.getname()); contentvalues.put("phone", person.getphone()); contentvalues.put("salary",person.getsalary()); uri inserturi=uri.parse("content://cn.bs.testcontentprovider/person"); uri returnuri=mcontentresolver.insert(inserturi, contentvalues); system.out.println("新增数据:returnuri="+returnuri); } private void testdelete(int index){ uri uri=uri.parse("content://cn.bs.testcontentprovider/person/"+string.valueof(index)); mcontentresolver.delete(uri, null, null); } private void testupdate(int index){ uri uri=uri.parse("content://cn.bs.testcontentprovider/person/"+string.valueof(index)); contentvalues values=new contentvalues(); values.put("name", "hanmeimei"); values.put("phone", "1234"); values.put("salary", 333); mcontentresolver.update(uri, values, null, null); } private void testquery(int index) { uri uri=null; if (index<=0) { //查询表 uri=uri.parse("content://cn.bs.testcontentprovider/person"); } else { //按照id查询某条数据 uri=uri.parse("content://cn.bs.testcontentprovider/person/"+string.valueof(index)); } //对应上面的:查询表 //cursor cursor= mcontentresolver.query(uri, null, null, null, null); //对应上面的:查询personid=2的数据 //注意:因为name是varchar字段的,所以应该写作"name='xiaoming1'" // 若写成"name=xiaoming1"查询时会报错 cursor cursor= mcontentresolver.query(uri, null, "name='xiaoming1'", null, null); while(cursor.movetonext()){ int personid=cursor.getint(cursor.getcolumnindex("personid")); string name=cursor.getstring(cursor.getcolumnindex("name")); string phone=cursor.getstring(cursor.getcolumnindex("phone")); int salary=cursor.getint(cursor.getcolumnindex("salary")); system.out.println("查询得到:personid=" + personid+",name="+name+",phone="+phone+",salary="+salary); } cursor.close(); } private void testtype(){ uri diruri=uri.parse("content://cn.bs.testcontentprovider/person"); string dirtype=mcontentresolver.gettype(diruri); system.out.println("dirtype:"+dirtype); uri itemuri=uri.parse("content://cn.bs.testcontentprovider/person/3"); string itemtype=mcontentresolver.gettype(itemuri); system.out.println("itemtype:"+itemtype); } }
person如下:
package cn.testbaidu; public class person { private integer id; private string name; private string phone; private integer salary; public person(string name, string phone,integer salary) { this.name = name; this.phone = phone; this.salary=salary; } public person(integer id, string name, string phone,integer salary) { this.id = id; this.name = name; this.phone = phone; this.salary=salary; } public integer getid() { return id; } public void setid(integer id) { this.id = id; } public string getname() { return name; } public void setname(string name) { this.name = name; } public string getphone() { return phone; } public void setphone(string phone) { this.phone = phone; } public integer getsalary() { return salary; } public void setsalary(integer salary) { this.salary = salary; } @override public string tostring() { return "person [id=" + id + ", name=" + name + ", phone=" + phone+ ", salary=" + salary + "]"; } }
main.xml如下:
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <button android:id="@+id/addbutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerhorizontal="true" android:layout_margintop="30dip" android:text="增加" android:textsize="20sp" /> <button android:id="@+id/querybutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerhorizontal="true" android:layout_margintop="30dip" android:layout_below="@id/addbutton" android:text="查询" android:textsize="20sp" /> <button android:id="@+id/deletebutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerhorizontal="true" android:layout_margintop="30dip" android:layout_below="@id/querybutton" android:text="删除" android:textsize="20sp" /> <button android:id="@+id/updatebutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerhorizontal="true" android:layout_margintop="30dip" android:layout_below="@id/deletebutton" android:text="修改" android:textsize="20sp" /> <button android:id="@+id/typebutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerhorizontal="true" android:layout_margintop="30dip" android:layout_below="@id/updatebutton" android:text="类型" android:textsize="20sp" /> </relativelayout>
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!