Android 中自定义ContentProvider与ContentObserver的使用简单实例

时间:2022-07-28
本文章向大家介绍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  

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!