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