一、引入:
数据库在Android当中是私有的,不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问。这意味着只有创建这个数据库的应用程序才可访问它。也就是说不能跨越进程和包的边界,直接访问别的应用程序的数据库。那么如何在应用程序间交换数据呢? 如果需要在进程间传递数据,可以使用ContentProvider来实现。
二、ContentProvider的功能和意义
为了在应用程序之间交换数据,Android提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API。当一个应用程序需要把自己的数据暴露给其他应用程序使用时,该应用程序可以通过提供ContentProvider来实现;而其他应用程序需要使用这些数据时,可以通过ContentResolver来操作ContentProvider暴露的数据。
一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过该接口来操作被暴露的内部数据,包括增加数据、删除数据、修改数据、查询数据等。
虽然大部分使用ContentProvider操作的数据都来自于数据库,但是也可以来自于文件、SharedPreferences、XML或网络等其他存储方式。
三、核心类:
ContentProvider:(A应用暴露数据)
一个程序可以通过实现一个ContentProvider的接口将自己的数据暴露出去;
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以修改程序的数据
ContentResolver:(操作A应用所暴露的数据)
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据;
ContentResolver 可以理解成是HttpClient的作用。
Uri:Uri是ContentResolver和ContentProvider进行数据交换的标识。
每个ContentProvider提供公共的URI来唯一标识其数据集。管理多个数据集的(多个表)的 ContentProvider 为每个数据集提供了单独的URI。
Uri 的authority部分:该部分是完整的类名。(使用小写形式)。
被请求的特定记录的id值。如果请求不仅限于某个单条数据,该部分及其前面的斜线应该删除。
为了将一个字符串转换成Uri,Android中提供了Uri的parse()静态方法来实现。
四、自定义ContentProvider:
(一)、操作步骤:
1、编写一个类,必须继承自ContentProvider类;
2、实现ContentProvider类中所有的抽象方法;
需要实现:onCreate() 、getType() 、query() 、insert() 、update()、delete() 等方法。
【备注:】
ContentProvider暴露出来的数据和方法并不是给自身调用的,而是给其他应用程序来调用。其他应用程序通过其ContentResolver对象调用的query() 、insert() 、update()、delete() 等方法就是我们在这里暴露出来的ContentProvider类中的重写后的query() 、insert() 、update()、delete() 方法。
3、定义ContentProvider的Uri。这个Uri是ContentResolver对象执行CRUD操作时重要的参数;
4、使用UriMatcher对象映射Uri返回代码;
5、在AndroidMainfest.xml文件中使用标签注册ContentProvider。
(二)、ContentProvider类中的六个抽象方法:
1、boolean onCreate()
Notice that your provider is not created until a ContentResolver object tries to access it.
ContentResolver不访问内容提供者的时候,内容提供者是不会创建出来的.
2、Uri insert(Uri uri, ContentValues values)
3、int delete(Uri uri, String selection, String[] selectionArgs)
4、int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
5、Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
6、String getType(Uri uri)
(三)、ContentProvider类中六个抽象方法的说明:
1、onCreate() 初始化provider
2、query() 返回数据给调用者
3、insert() 插入新数据到ContentProvider
4、update() 更新ContentProvider已经存在的数据
5、delete() 从ContentProvider中删除数据
6、getType() 返回ContentProvider数据的MIME类型
(四)、在清单文件中声明注册ContentProvider:
".MyWordsProvider"
android:authorities="com.steven.wordscontentprovider"
android:exported="true"
/>
//android:name属性的值是:ContentProvider类的子类的完整路径;
//android:authorities属性的值是:content:URI中authority部分。一般就是将name属性的值全小写。
//android:exported属性是否允许其他应用调用。如果是false,则该ContentProvider不允许其他应用调用。
(五)、UriMatcher:
继承ContentProvider类后发现,ContentProvider类中只有一个onCreate()生命周期方法——当其他应用程序通过ContentResolver第一次访问该ContentProvider时,onCreate()会被回调。
其他应用在通过ContentResolver对象执行CRUD操作时,都需要一个重要的参数Uri。为了能顺利提供这个Uri参数,Android系统提供了一个UriMatcher工具类。
UriMatcher工具类提供了两个方法:
1、void addURI(String authority , String path , int code) : 该方法用于向UriMatcher对象注册Uri。其中authority和path是Uri中的重要部分。而code代表该Uri对应的标示符。
2、int match(Uri uri) : 根据注册的Uri来判断指定的Uri对应的标示符。如果找不到匹配的标示符,该方法返回-1。
private static UriMatcher matcher = null;
static {
// 定义一个Uri匹配器。将UriMatcher.NO_MATCH,即-1作为参数。
matcher = new UriMatcher(UriMatcher.NO_MATCH);
// 定义一组匹配规则
matcher.addURI(AUTHORITY, "words", 1);
matcher.addURI(AUTHORITY, "newwords", 2);
}
【备注:】
ContentProvider是单例模式的,当多个应用程序通过使用ContentResolver 来操作使用ContentProvider 提供的数据时,ContentResolver 调用的数据操作会委托给同一个ContentProvider 来处理。这样就能保证数据的一致性。
五.demo
1.简介:该demo包含两个应用,一个为c1,它将接口暴露出去;一个是c2,它操作c1
2.c1的部分代码清单:
清单配置文件中注册ContentProvider:
//ContentProvider类的子类的完整路径
android:name=".MyContentProvider"
// 一般就是将name属性的值全小写
android:authorities="com.example.c1.mycontentprovider"
android:exported="true" />
public class MyContentProvider extends ContentProvider {
private static UriMatcher uriMatcher;
private final String TABLE_NAME = "tb_words";
//和android:authorities属性一致
public static final String AUTHORITY = "com.example.c1.mycontentprovider";
private SQLiteDatabase db = null;
@Override
public boolean onCreate() {
db = SQLiteDatabase.openOrCreateDatabase(
"/data/data/com.example.c1/test.db3", null);
db.execSQL("create table IF NOT EXISTS tb_words (_id integer primary key autoincrement,word text,detall text);");
return true;
}
static {
uriMatcher = new UriMatcher(-1);
uriMatcher.addURI(AUTHORITY, "words", 1);
uriMatcher.addURI(AUTHORITY, "newwords", 2);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.d("c1", "uri=" + uri);
Log.d("c1", "selection=" + selection);
Log.d("c1", "selectionArgs=" + selectionArgs);
int count = 0;
switch (uriMatcher.match(uri)) {
case 1:
count = db.delete(TABLE_NAME, selection, selectionArgs);
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
}
return count;
}
@Override
public String getType(Uri arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
String word = values.get("word").toString();
String detall = values.get("detall").toString();
Log.d("c1", "uri:" + uri + ";word=" + word + ";detall" + detall
+ ":uriMatcher.match(uri)=" + uriMatcher.match(uri));
long rowId = 0;
switch (uriMatcher.match(uri)) {
case 1:
rowId = db.insert(TABLE_NAME, null, values);
Log.d("c1", "rowId:" + rowId);
if (rowId > 0) {
Uri newUri = ContentUris.withAppendedId(
Uri.parse("content://" + AUTHORITY + "/words"), rowId);
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
break;
case 2:
rowId = db.insert("tb_newwords", null, values);
if (rowId > 0) {
Uri newUri = ContentUris.withAppendedId(
Uri.parse("content://" + AUTHORITY + "/newwords"),
rowId);
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
break;
default:
break;
}
return null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor query = null;
Log.d("c1", "projection=" + projection);
Log.d("c1", "selection=" + selection);
Log.d("c1", "selectionArgs=" + selectionArgs);
Log.d("c1", "sortOrder=" + sortOrder);
switch (uriMatcher.match(uri)) {
case 1:
query = db.query(TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
}
return query;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
// TODO Auto-generated method stub
return 0;
}
}
c2:
public static final String AUTHORITY = "com.example.c1.mycontentprovider";
private ContentResolver contentResolver;
private MyContentObserver mMyContentObserver;
private Button button;
private Button button1;
private Button button2;
private Uri uri;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0x110:
Toast.makeText(getApplicationContext(),
"insert:" + msg.obj.toString(), 1000).show();
break;
case 0x111:
Toast.makeText(getApplicationContext(),
"delete:" + msg.obj.toString(), 1000).show();
break;
case 0x112:
Toast.makeText(getApplicationContext(),
"find:" + msg.obj.toString(), 1000).show();
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button1);
button1 = (Button) findViewById(R.id.button2);
button2 = (Button) findViewById(R.id.button3);
uri = Uri.parse("content://" + AUTHORITY + "/words");
contentResolver = this.getContentResolver();
mMyContentObserver = new MyContentObserver(handler);
// contentResolver.registerContentObserver(Uri.parse("content://" +
// AUTHORITY + "/words"), true,
// mMyContentObserver);
// add
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
ContentValues values = new ContentValues();
values.put(Words.Word.WORD, "word" + Math.random());
values.put(Words.Word.DETALL, "detall" + Math.random());
Uri insert = contentResolver.insert(uri, values);
sengMsg(0x110, insert);
}
});
// delete
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
int delete = contentResolver.delete(uri, "_id>?",
new String[] { "5" });
sengMsg(0x111, delete);
}
});
// find
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
/**
* 第一个参数:指定内容提供者的Uri地址
* 第二个参数:筛选返回的结果
* 第三个参数:一个简化版的sql where语句
* 第四个参数:配合第三个参数使用,你可以在第三个参数中使用占位符"?",那么在第四个参数据会替换掉占位符
* 第五个参数:一个简化的sql排序语句:
*/
Cursor query = contentResolver.query(uri,
new String[] { "word" }, "_id>?", new String[] { "5" },
null);
sengMsg(0x112, query.getCount());
}
});
}
private void sengMsg(int what, Object obj) {
Message msg = new Message();
msg.what = what;
msg.obj = obj;
handler.sendMessage(msg);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
//该类继承ContentObserver,当注册contentResolver.registerContentObserver时,ContentProvider发出getContext().getContentResolver().notifyChange(uri, null)时,onChange方法会接收到相关信息,比如说:有一个天气预报的apk,当天气发生变化的时候,ContentProvider会notifyChange,这时候,MyContentObserver就会知道天气已经变化了,需要处理相关逻辑
class MyContentObserver extends ContentObserver {
private Handler mHandler = null;
public MyContentObserver(Handler handler) {
super(handler);
mHandler = handler;
}
@Override
public void onChange(boolean selChange) {
// Message msg = new Message();
// msg.what = 0x110;
// mHandler.sendMessage(msg);
}
}
}
权限使用:
http://blog.csdn.net/anddlecn/article/details/51733690
http://iwillbemyself.blog.163.com/blog/static/1702232282012115104056917/