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无法启动。