转载请注明出处:http://blog.csdn.net/dmk877/article/details/50387741
苦心人天不负卧薪尝胆三千越甲可吞吴,有志者天不负釜底抽薪百二秦川终属楚。这是一对非常励志的名言,每当读这句话都会被震撼一下,然后接着颓废,哈哈,最近的工作比较忙,也在这里提醒自己,一定要坚持下去,一定要坚持一件对自己有益的事情。
装逼到此进入正题,今天要讨论的主要内容是ContentProvider(内容提供者),ContentProvider也是android的四大组件之一,可见其在android中的重要性,可能大家用ContentProvider比其他三个组件用的少一点,但是ContentProvider同样的非常重要,有的人知道怎么使用ContentProvider,但是对于ContentProvider的原理等,并没有搞清楚,没关系,通过本篇博客相信你会对ContentProvider有一个全新的认识。
通过本篇博客你将学到以下知识
①什么是内容提供者
②为什么会有内容提供者
③怎样使用内容提供者
④ContentProvider中的Uri的详细介绍
⑤ContentResolver讲解
⑥UriMatch用法介绍
⑦ContentObserver用法详解
⑧通过一个案例来讲解自定义ContentProvider的执行过程(下一篇将给大家带来调用系统的ContentProvider)
1.什么是内容提供者?
首先我们必须要明白的是ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用比较少。ContentProvider为不同的软件之间数据共享,提供统一的接口。而且ContentProvider是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。至于如何从URI中识别出外界需要的是哪个“数据库”这就是Android底层需要做的事情了,也就是说,如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用我们自己应用中的文件、数据库内存储的信息。当然,自己开发的应用需要给其他应用共享信息的需求可能比较少见,但是在Android系统中,很多数据如:联系人信息、短信信息、图片库、音频库等,这些信息在开发中还是经常用到的,这些信息谷歌工程师已经帮我们封装好了,我们可以使用谷歌给我的Uri去直接访问这些数据。所以对于ContentProvider我们还是需要认真的学习的,在遇到获取联系人信息,图片库,音视频库等需求的时候,才能更好的实现功能。
它的作用是在ContentProvider添加一个用于匹配的Uri,当匹配成功时返回code。Uri可以是精确的字符串,Uri中带有*表示可匹配任意text,#表示只能匹配数字。
③public int match(Uri uri) 这里的Uri就是传过来的要进行验证,匹配的Uri假如传过来的是:content://com.example.test/student/#,则content://com.example.test/student/10可以匹配成功,这里的10可以使任意的数字。
public final void registerContentObserver (Uri uri, boolean notifyForDescendents, ContentObserver observer)
注册一个观察者实例,当指定的Uri发生改变时,这个实例会回调实例对象做相应处理。
参数:uri:需要观察的Uri
notifyForDescendents:如果为true表示以这个Uri为开头的所有Uri都会被匹配到,
如果为false表示精确匹配,即只会匹配这个给定的Uri。
举个例子,假如有这么几个Uri:
①content://com.example.studentProvider/student
②content://com.example.studentProvider/student/#
③content://com.example.studentProvider/student/10
④content://com.example.studentProvider/student/teacher
假如观察的Uri为content://com.example.studentProvider/student,当notifyForDescendents为true时则以这个Uri开头的Uri的数据变化时都会被捕捉到,在这里也就是①②③④的Uri的数据的变化都能被捕捉到,当notifyForDescendents为false时则只有①中Uri变化时才能被捕捉到。
看到registerContentObserver 这个方法,根据语言基础我想大家能够想到ContentResolver中的另一个方法
public final voidunregisterContentObserver(ContentObserverobserver)它的作用就是取消对注册的那个Uri的观察,这里传进去的就是在registerContentObserver中传递进去的ContentObserver对象。到这关于注册和解除注册的ContentObserver可能大家都比较清楚了,那么问题来了,怎么去写一个ContentObserver呢?其实它的实现很简单,直接创建一个类继承ContentObserver需要注意的是这里必须要实现它的构造方法
public ContentObserver(Handlerhandler)
这里传进去的是一个Handler对象,这个Handler对象的作用一般要依赖于ContentObserver的另一个方法即
public void onChange(boolean selfChange)
这个方法的作用就是当指定的Uri的数据发生变化时会回调该方法,此时可以借助构造方法中的Handler对象将这个变化的消息发送给主线程,当主线程接收到这个消息之后就可以按照我们的需求来完成相应的操作,比如上面提到的类似于Adapter的notifyDataSetChanged()的作用,下面的案例也是完成了这个功能,准备工作完成之后来看一个案例,相信这个案例会让你对以上知识了解的更加深入。
package com.example.contentproviderpractice;
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 PeopleContentProvider extends ContentProvider {
//这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities,这里的authorities可以随便写
private static final String AUTHORITY = "com.example.studentProvider";
//匹配成功后的匹配码
private static final int MATCH_ALL_CODE = 100;
private static final int MATCH_ONE_CODE = 101;
private static UriMatcher uriMatcher;
private SQLiteDatabase db;
private DBOpenHelper openHelper;
private Cursor cursor = null;
//数据改变后指定通知的Uri
private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/student");
//在静态代码块中添加要匹配的 Uri
static {
//匹配不成功返回NO_MATCH(-1)
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* uriMatcher.addURI(authority, path, code); 其中
* authority:主机名(用于唯一标示一个ContentProvider,这个需要和清单文件中的authorities属性相同)
* path:路径路径(可以用来表示我们要操作的数据,路径的构建应根据业务而定)
* code:返回值(用于匹配uri的时候,作为匹配成功的返回值)
*/
uriMatcher.addURI(AUTHORITY, "student", MATCH_ALL_CODE);// 匹配记录集合
uriMatcher.addURI(AUTHORITY, "student/#", MATCH_ONE_CODE);// 匹配单条记录
}
@Override
public boolean onCreate() {
openHelper = new DBOpenHelper(getContext());
db = openHelper.getWritableDatabase();
return false;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
/**
* 这里如果匹配是uriMatcher.addURI(AUTHORITY, "student",
* MATCH_SUCCESS_CODE);中的Uri,则我们可以在这里对这个ContentProvider中的数据库
* 进行删除等操作。这里如果匹配成功,我们将删除所有的数据
*/
case MATCH_ALL_CODE:
int count=db.delete("personData", null, null);
if(count>0){
notifyDataChanged();
return count;
}
break;
/**
* 这里如果匹配是uriMatcher.addURI(AUTHORITY,
* "student/#",MATCH_ONE_CODE);中的Uri,则说明我们要操作单条记录
*/
case MATCH_ONE_CODE:
// 这里可以做删除单条数据的操作。
break;
default:
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
/**
* 插入 使用UriMatch的实例中的match方法对传过来的 Uri进行匹配。 这里通过ContentResolver传过来一个Uri,
* 用这个传过来的Uri跟在ContentProvider中静态代码块中uriMatcher.addURI加入的Uri进行匹配
* 根据匹配的是否成功会返回相应的值,在上述静态代码块中调用uriMatcher.addURI(AUTHORITY,
* "student",MATCH_CODE)这里的MATCH_CODE
* 就是匹配成功的返回值,也就是说假如返回了MATCH_CODE就表示这个Uri匹配成功了
* ,我们就可以按照我们的需求就行操作了,这里uriMatcher.addURI(AUTHORITY,
* "person/data",MATCH_CODE)加入的Uri为:
* content://com.example.studentProvider/student
* ,如果传过来的Uri跟这个Uri能够匹配成功,就会按照我们设定的步骤去执行相应的操作
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
int match=uriMatcher.match(uri);
if(match!=MATCH_ALL_CODE){
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
long rawId = db.insert("personData", null, values);
Uri insertUri = ContentUris.withAppendedId(uri, rawId);
if(rawId>0){
notifyDataChanged();
return insertUri;
}
return null;
}
/**
* 查询 如果uri为
* content://com.example.studentProvider/student则能匹配成功,然后我们可以按照需求执行匹配成功的操作
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (uriMatcher.match(uri)) {
/**
* 如果匹配成功,就根据条件查询数据并将查询出的cursor返回
*/
case MATCH_ALL_CODE:
cursor = db.query("personData", null, null, null, null, null, null);
break;
case MATCH_ONE_CODE:
// 根据条件查询一条数据。。。。
break;
default:
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case MATCH_ONE_CODE:
long age = ContentUris.parseId(uri);
selection = "age = ?";
selectionArgs = new String[] { String.valueOf(age) };
int count = db.update("personData", values, selection,selectionArgs);
if(count>0){
notifyDataChanged();
}
break;
case MATCH_ALL_CODE:
// 如果有需求的话,可以对整个表进行操作
break;
default:
throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
}
return 0;
}
//通知指定URI数据已改变
private void notifyDataChanged() {
getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
}
}
可以看到在onCreate()方法中创建了一个数据库,关于数据库的操作大家可以看此博客 http://blog.csdn.net/dmk877/article/details/44876805。这里就不多做介绍了。
注意这个案例牵扯到两个项目,一个是包含我们自定义的ContentProvider,另一个项目是访问这个包含ContentProvider项目中的数据。
package com.example.otherapplication;
import java.util.ArrayList;
import com.example.otherapplication.adapter.MyAdapter;
import com.example.otherapplication.bean.Student;
import com.example.otherapplication.observer.PersonOberserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
public class MainActivity extends Activity implements OnClickListener {
private ContentResolver contentResolver;
private ListView lvShowInfo;
private MyAdapter adapter;
private Button btnInit;
private Button btnInsert;
private Button btnDelete;
private Button btnUpdate;
private Button btnQuery;
private Cursor cursor;
private static final String AUTHORITY = "com.example.studentProvider";
private static final Uri STUDENT_ALL_URI = Uri.parse("content://" + AUTHORITY + "/student");
protected static final String TAG = "MainActivity";
private Handler handler=new Handler(){
public void handleMessage(android.os.Message msg) {
//在此我们可以针对数据改变后做一些操作,比方说Adapter.notifyDataSetChanged()等,根据业务需求来定。。
cursor = contentResolver.query(STUDENT_ALL_URI, null, null, null,null);
adapter.changeCursor(cursor);
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lvShowInfo=(ListView) findViewById(R.id.lv_show_info);
initData();
}
private void initData() {
btnInit=(Button) findViewById(R.id.btn_init);
btnInsert=(Button) findViewById(R.id.btn_insert);
btnDelete=(Button) findViewById(R.id.btn_delete);
btnUpdate=(Button) findViewById(R.id.btn_update);
btnQuery=(Button) findViewById(R.id.btn_query);
btnInit.setOnClickListener(this);
btnInsert.setOnClickListener(this);
btnDelete.setOnClickListener(this);
btnUpdate.setOnClickListener(this);
btnQuery.setOnClickListener(this);
contentResolver = getContentResolver();
//注册内容观察者
contentResolver.registerContentObserver(STUDENT_ALL_URI,true,new PersonOberserver(handler));
adapter=new MyAdapter(MainActivity.this,cursor);
lvShowInfo.setAdapter(adapter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//初始化
case R.id.btn_init:
ArrayList students = new ArrayList();
Student student1 = new Student("苍老师",25,"一个会教学的好老师");
Student student2 = new Student("柳岩",26,"大方");
Student student3 = new Student("杨幂",27,"漂亮");
Student student4 = new Student("张馨予",28,"不知道怎么评价");
Student student5 = new Student("范冰冰",29,"。。。");
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
students.add(student5);
for (Student Student : students) {
ContentValues values = new ContentValues();
values.put("name", Student.getName());
values.put("age", Student.getAge());
values.put("introduce", Student.getIntroduce());
contentResolver.insert(STUDENT_ALL_URI, values);
}
break;
//增
case R.id.btn_insert:
Student student = new Student("小明", 26, "帅气男人");
//实例化一个ContentValues对象
ContentValues insertContentValues = new ContentValues();
insertContentValues.put("name",student.getName());
insertContentValues.put("age",student.getAge());
insertContentValues.put("introduce",student.getIntroduce());
//这里的uri和ContentValues对象经过一系列处理之后会传到ContentProvider中的insert方法中,
//在我们自定义的ContentProvider中进行匹配操作
contentResolver.insert(STUDENT_ALL_URI,insertContentValues);
break;
//删
case R.id.btn_delete:
//删除所有条目
contentResolver.delete(STUDENT_ALL_URI, null, null);
//删除_id为1的记录
Uri delUri = ContentUris.withAppendedId(STUDENT_ALL_URI,1);
contentResolver.delete(delUri, null, null);
break;
//改
case R.id.btn_update:
ContentValues contentValues = new ContentValues();
contentValues.put("introduce","性感");
//更新数据,将age=26的条目的introduce更新为"性感",原来age=26的introduce为"大方".
//生成的Uri为:content://com.example.studentProvider/student/26
Uri updateUri = ContentUris.withAppendedId(STUDENT_ALL_URI,26);
contentResolver.update(updateUri,contentValues, null, null);
break;
//查
case R.id.btn_query:
//通过ContentResolver获得一个调用ContentProvider对象
Cursor cursor = contentResolver.query(STUDENT_ALL_URI, null, null, null,null);
//CursorAdapter的用法,参考此博客:http://blog.csdn.net/dmk877/article/details/44983491
adapter=new MyAdapter(MainActivity.this,cursor);
lvShowInfo.setAdapter(adapter);
cursor = contentResolver.query(STUDENT_ALL_URI, null, null, null,null);
adapter.changeCursor(cursor);
break;
}
}
}
package com.example.otherapplication.observer;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Message;
public class PersonOberserver extends ContentObserver {
private Handler handler;
public PersonOberserver(Handler handler) {
super(handler);
this.handler=handler;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//向handler发送消息,更新查询记录
Message msg = new Message();
handler.sendMessage(msg);
}
}
可以看到,在构造方法中接收了Handler然后当监听到指定的Uri的数据变化时就会通过Handler消息机制发送一条消息,然后的操作就由我们自行完成了。