自定义ContentProvider详解

ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查。


要注意ContentProvider的作用,是为别的应用调用本应用中的数据或者文件提供接口,而它也是唯一的跨应用数据传递的接口。

如果仅仅是同一个应用中的数据传递,则完全没有必要使用到自定义的ContentProvider。

关于URI:

Android各种类型的URI基本上都是有固定格式的,对于ContentProvider而言,由三部分构成一般形如

content://cn.itcast.sqlite.provider/person/1

红色:scheme协议,固定写法

黄色:主机名或authority 

path:表示路径,对于数据库来说,表示操作person表下id为1的记录

下面来看一个例子

比如说B应用要访问A应用中的数据库中的一张表。

1.首先在A应用中要写一个java类,继承ContentProvider,重写insert、delete、query、update、getType、onCreate方法,如下:

package cn.itcast.sqlite.provider;

import cn.itcast.sqlite.dao.DBOpenHelper;

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 SQLiteProvider extends ContentProvider {

	private static final int PERSON = 1;
	private static final int PERSON_ID = 2;
	private UriMatcher matcher; 				 							// uri匹配器,用来解析uri
	private DBOpenHelper dbhelper;

	 // 程序第一次启动时执行,会驻留在后台,除非结束程序进程,再开启在此执行次此方法
	public boolean onCreate() {				
		System.out.println("创建内容提供者,执行onCreate");
		matcher = new UriMatcher(UriMatcher.NO_MATCH);
		dbhelper = new DBOpenHelper(getContext());
		matcher.addURI("cn.itcast.sqlite.provider", "person", PERSON);
		matcher.addURI("cn.itcast.sqlite.provider", "person/#", PERSON_ID);
		return true;
	}

	//查询数据
	public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db = dbhelper.getReadableDatabase();
		switch (matcher.match(uri)) {
		case PERSON_ID:
			long id = ContentUris.parseId(uri); // 获取传入过来的id
			selection = "id=" + id;
		case PERSON:
			return db.query("person", projection, selection, selectionArgs,
					null, null, sortOrder);
		default:
			throw new RuntimeException("没有匹配的uri");
		}
	}

	//插入数据
	public Uri insert(Uri uri, ContentValues values) {
		System.out.println("insert");
		SQLiteDatabase db = dbhelper.getReadableDatabase();
		switch (matcher.match(uri)) {
		case PERSON:
			long id = db.insert("person", "id", values);
			getContext().getContentResolver().notifyChange(uri, null); // 通知观察者数据进行了修改
			return ContentUris.withAppendedId(uri, id);
		default:
			throw new RuntimeException("Uri不能识别" + uri);
		}
	}

	//删除数据
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		System.out.println("delete");
		SQLiteDatabase db = dbhelper.getWritableDatabase();
		switch (matcher.match(uri)) {
		case PERSON_ID:
			long id = ContentUris.parseId(uri); // 获取传入过来的id
			selection = selection == null ? "id=" + id : selection + "AND id="
					+ id;
		case PERSON:
			int updid = db.delete("person", selection, selectionArgs);
			getContext().getContentResolver().notifyChange(uri, null); // 通知观察者数据进行了修改
			return updid;
		default:
			throw new RuntimeException("没有匹配的uri");
		}
	}

	//更新数据
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		SQLiteDatabase db = dbhelper.getWritableDatabase();
		switch (matcher.match(uri)) {
		case PERSON_ID:
			long id = ContentUris.parseId(uri); // 获取id
			getContext().getContentResolver().notifyChange(uri, null); // 通知观察者数据进行了修改
			selection = selection == null ? "id=" + id : selection + "AND id="
					+ id;
		case PERSON:
			return db.update("person", values, selection, selectionArgs);
		default:
			throw new RuntimeException("Uri不能识别" + uri);
		}
	}

	
	public String getType(Uri uri) {
		// TODO Auto-generated method stub
		switch (matcher.match(uri)) {
		case PERSON_ID:
			return "vnd.android.cursor.item/person";// 带了id,操作指定person
		case PERSON:
			return "vnd.android.cursor.dir/person";// 没带ID,操作所有person

		default:
			throw new RuntimeException("Uri不能识别" + uri);
		}
	}

}
在这个类中,如果需要及时通知你的应用别的应用通过ContentProvider访问你,比如说,B应用通过ContentProvider往你数据库插入了一条数据,A应用要及时更新,可以在A应用的Activity中注册一个内容观察者ContentObserver,然后在ContentProvider对应方法中通知观察者数据进行了修改(代码如上),在Activity中注册ContentObserver具体如下:

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        dao = new PersonDao(this);
        persons = dao.queryAll();					// 获取数据
        
        personLV = (ListView) findViewById(R.id.personLV);		// 获取ListView
        personLV.setAdapter(new MyBaseAdapter());			// 给ListView添加Adapter, 按照Adapter中的方法对ListView添加条目
        
        personLV.setOnItemClickListener(new MyOnItemClickListener());	// 给ListView添加条目点击监听器
        
        //注册方法
        Uri uri=Uri.parse("content://cn.itcast.sqlite.provider");
        getContentResolver().registerContentObserver(uri, true, new myContentObeserver()); //监听指定uri(包含子路径)的修改
    }
	private class myContentObeserver extends ContentObserver{
		
		public myContentObeserver(){
			super(new Handler());			//Handler是一个处理器,目前没有用到
		}
		public void onChange(boolean selfChange){	//当ContentProvider内容改变,执行此方法
			System.out.println("数据改变了");
			persons=dao.queryAll();			//重新查询数据
			personLV.setAdapter(new MyBaseAdapter());//设置新的ListView
		}
	

然后要在AndroidManifest.xml中声明provider,在Application下,与activity同级

 <provider
            android:name=".provider.SQLiteProvider" 
            android:authorities="cn.itcast.sqlite.provider"
  />


2.在B应用中写一个测试类,测试访问A应用中数据库中的person表


import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;

public class ProviderTest extends AndroidTestCase {
	
	public void testQueryAll() {
		ContentResolver resolver = getContext().getContentResolver();
		Uri uri = Uri.parse("content://cn.itcast.sqlite.provider/person");
		Cursor c = resolver.query(uri, new String[]{"id","name","balance"}, "balance>?", new String[]{ 5000 + ""}, "balance DESC");
		while (c.moveToNext()) {
			Person p = new Person(c.getInt(0), c.getString(1), c.getInt(2));
			System.out.println(p);
		}
	}
	public void testQueryOne() {
		ContentResolver resolver = getContext().getContentResolver();
		Uri uri = Uri.parse("content://cn.itcast.sqlite.provider/person/2");
		Cursor c = resolver.query(uri, null, null,null,null);
		while (c.moveToNext()) {
			Person p = new Person(c.getInt(0), c.getString(1), c.getInt(2));
			System.out.println(p);
		}
	}
	public void testInsert () {
		ContentResolver resolver = getContext().getContentResolver();
		Uri uri = Uri.parse("content://cn.itcast.sqlite.provider/person");
		ContentValues values=new ContentValues();
		values.put("name", "ob3");
		values.put("balance", 123456);
		uri=resolver.insert(uri, values);
		System.out.println(uri);
	}
	public void testUpdate () {
		ContentResolver resolver = getContext().getContentResolver();
		Uri uri = Uri.parse("content://cn.itcast.sqlite.provider/person/13");//如果后面不带id,则更新所有
		ContentValues values=new ContentValues();
		values.put("name", "update");
		values.put("balance", 654321);
		int count=resolver.update(uri, values, null, null);				//更新了几条
		System.out.println(count);
		
	}
	public void testDelete() {
		ContentResolver resolver = getContext().getContentResolver();
		Uri uri = Uri.parse("content://cn.itcast.sqlite.provider/person/13");//如果后面不带id,则删除所有
		int count=resolver.delete(uri, null, null);				//删除了几条
		System.out.println(count);
		
	}
	public void testGetType(){
		ContentResolver resolver = getContext().getContentResolver();
		String type1=resolver.getType(Uri.parse("content://cn.itcast.sqlite.provider/person"));
		String type2=resolver.getType(Uri.parse("content://cn.itcast.sqlite.provider/person/2"));
		System.out.println(type1);
		System.out.println(type2);
	}
	
}

最后,关于ContentProvider中的GetType()方法,一直不知道有啥用,找到资料说是如下:

它的作用是根据URI返回该URI所对应的数据的MIME类型字符串。这种字符串的格式分为两段:“A/B”。其中A段是固定的,集合类型(如多条数据)必须是vnd.android.cursor.dir,非集合类型(如单条数据)必须是vnd.android.cursor.item;B段可以是自定义的任意字符串;A、B两段通过“/”隔开。这个MIME类型字符串的作用是要匹配AndroidManifest.xml文件<activity>标签下<intent-filter>标签的子标签<data>的属性android:mimeType。如果不一致,则会导致对应的Activity无法启动。



原文地址:https://www.cnblogs.com/fzll/p/3954619.html