自定义ContentProvider以及ContentObserver的使用完整详细示例
发布日期:2021-06-30 11:16:26 浏览次数:2 分类:技术文章

本文共 14444 字,大约阅读时间需要 48 分钟。

示例说明:

该示例中一共包含两个工程。其中一个工程完成了自定义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如下:
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如下:

main.xml如下:

以下代码为工程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如下:

转载地址:https://it9527.blog.csdn.net/article/details/42003815 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android事件分发详解(六)——ACTION_DOWN的消费验证
下一篇:SQLite数据库版本升级的管理实现

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月05日 23时22分05秒