contenProvider模块知识
大纲
名称 | ContentProvider(内容提供者) | ContentResolver(内容解析者) |
---|---|---|
作用 | 1. Android中的四大组件之一 2.用于应用程序之间的数据共享 3.将数据的访问方式统一 | 1.用于从ContentProvider提供的接口中获取数据 2.对ContentProvider提供的数据进行增删改查 |
关系 | 提供数据,用于数据共享 | 获取数据,进行数据操作 |
URI的概念
-
URI是一个标识符,为字符串类型
-
由scheme、authorites、path三部分组成
content://cn.itcast.sqlite.provider/person
scheme:固定为content,代表访问内容提供者
authorites:节点中的authorites属性
一般为包名.类名
使用场景
1.定义Uri
2.在清单文件中注册ContentProvider
path:程序定义的路径,可根据业务逻辑定义
MIME(多用途互联网邮件扩展类型)
定义:MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。它是一个互联网标准,扩展了电子邮件标准,使其能够支持:
例如:左边是文件的具体大类型,右边是具体的文件类型
超文本标记语言文本 .html
text/html
xml文档 .xml
text/xml
XHTML文档 .xhtml
application/xhtml+xml
普通文本 .txt
text/plain
常用的辅助类
UriMatch类
用于注册和匹配给定的URI
方法 | 说明 |
---|---|
addURI(String authority, String path, int code) | code:表示匹配成功后的返回值 |
match(Uri uri) | 匹配Uri |
ContentUris
Uri工具类,用于处理Uri尾部的id
方法 | 说明 |
---|---|
withAppendId | 将id添加到Uri的尾部,并将其返还给客户 |
parseId | 获取path的数据 |
ContentObserver
内容观察者,用于ContentProvider提供的数据发生变化时,执行回调更新。
我们在ContentProvider的insert,update,delete等改变之后调用getContext().getContentResolver().notifyChange(uri, null);这样就通知那些监测databases变化的observer了,而你的observer可以在一个service里面注册。
方法 | 说明 |
---|---|
registerContentObserve(Uri uri, boolean notifyForDescendants, ContentObserver observer) | 注册 |
onChange(boolean selfChange) | 回调 |
注意
registerContentObserve()是ContentResolver类中的方法。
onChange(boolean selfChange)是ContentObserver中的方法。
自定义ContentProvider步骤
1.定义一个类,继承自ContentProvider
2.在清单文件中进行声明
3.定义一个类,继承自ContentObserver,实现onChange方法(可选)
代码实现
定义一个类
由于ContentProvider的创建涉及到数据库的建立,故要先写一个SQLiteOpenHelper来创建数据库
DbHelper的oncreate()的sql语句,新建一个数据库,并插入一条数据。
package com.example.contentproviderdemo2.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* Created by 15426 on 2020/9/27.
*/
public class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table person("
+"id integer primary key autoincrement,"
+"name varchar(20),"
+"age integer(5),"
+"sex integer(2),"
+"desc varchar(200))";
String sql2 ="insert into person (name, age, sex, desc) values(?,?,?,?)";
db.execSQL(sql);
db.execSQL(sql2,new String[]{
"barry","23","1","he is a man"
});
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
实现ContentProvider类,这里最主要的是,写好那些Uri,authority等常量。
package com.example.contentproviderdemo2.provider;
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;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.example.contentproviderdemo2.db.DbHelper;
/**
* 自定义ContentProvider来实现Person信息的增删改查
* ContentProvider的insert,update,delete等改变之后
* 调用getContext().getContentResolver().notifyChange(uri, null);这里传null,、
* 是说明不需要指定observer,,要在注册时传入自己定义的observer即可,这里就会自动找到相关的observer.
* 这样就通知那些监测databases变化的observer了,就会调用onChange()方法了。
* Created by 15426 on 2020/9/27.
*/
public class PersonProvider extends ContentProvider {
//所有常量都要用有意义的变量名表示出来。
private static final String PACKAGE_NAME = "com.example.contentproviderdemo2";
private static final String DB_NAME ="person.db";
private static final String TABLE_NAME = "person";
private static final int DB_VISION = 1;
//包名+类名
private static final String AUTHORITY = PACKAGE_NAME +".provider.PersonProvider";
private static final String CONTENT_TIPE = "vnd.android.cursor.dir/"+AUTHORITY+"."+TABLE_NAME;
private static final String CONTENT_ITEM_TYPE ="vnd.android.cursor.item/"+AUTHORITY+"."+TABLE_NAME;
private static final int DIR_CODE= 0;
private static final int ITEM_CODE =1;
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
private DbHelper dbHelper;//null
private static UriMatcher matcher;//null
static {
matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(AUTHORITY,TABLE_NAME,DIR_CODE);
matcher.addURI(AUTHORITY,TABLE_NAME+"/#",ITEM_CODE);
}
/**
* 初始化提供其,在这里完成对数据库的创建和升级
* @return true表示创建或升级成功。
*/
@Override
public boolean onCreate() {
dbHelper = new DbHelper(getContext(),DB_NAME,null,DB_VISION);
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder) {
//获取数据库
SQLiteDatabase db = dbHelper.getReadableDatabase();
//先对不同的uri进行处理,最后在统一的查询
selection = handlerSelection(uri, selection);
if(selection.equal(null)){
retrun null;
}
Cursor cursor= db.query(TABLE_NAME,projection,selection,selectionArgs,null,null,sortOrder);
return cursor;
}
private String handlerSelection(@NonNull Uri uri, @Nullable String selection) {
switch (matcher.match(uri)){
case DIR_CODE://集合不需要做任何处理
break;
case ITEM_CODE://单条记录需要处理尾部的id,并且和现有的查询条件进行拼接
long id = ContentUris.parseId(uri);
if (TextUtils.isEmpty(selection)){
selection = "id ="+ id;
}else {
selection += "and id ="+id;
}
break;
default:
return null;
}
return selection;
}
/**
* 获取Uri对象的类型
* @param uri
* @return
*/
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (matcher.match(uri)){
case DIR_CODE:
return CONTENT_TIPE;
case ITEM_CODE:
return CONTENT_ITEM_TYPE;
}
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
long newID = db.insert(TABLE_NAME,null,values);
//直接将id后缀到uri之中
Uri newUri = ContentUris.withAppendedId(CONTENT_URI,newID);
if (newUri !=null){
getContext().getContentResolver().notifyChange(newUri,null);
return newUri;
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
selection = handlerSelection(uri,selection);
int result = db.delete(TABLE_NAME,selection,selectionArgs);
if (result >0){
getContext().getContentResolver().notifyChange(uri,null);
return result;
}
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
selection = handlerSelection(uri,selection);
int result = db.update(TABLE_NAME,values,selection,selectionArgs);
if (result >0){
getContext().getContentResolver().notifyChange(uri,null);
return result;
}
return 0;
}
}
实现数据的自动更新
要在三个类中进行逻辑编写。
1.ContentResolver 的数据修改,其实是调用ContentProvider的insert, updete, delete,函数。
故可以根据这三个函数的返回结果,来判断是否发出给obeserver发出通知。
getContext().getContentResolver().notifyChange(uri, null);这样就通知那些监测databases变化的observer了。
insert函数
long newID = db.insert(TABLE_NAME,null,values);
//直接将id后缀到uri之中
Uri newUri = ContentUris.withAppendedId(CONTENT_URI,newID);
if (newUri !=null){
getContext().getContentResolver().notifyChange(newUri,null);
return newUri;
}
}
obeserver收到通知,会调用onChange
在observer类中,通常是进行异步传递机制。
由于obeserver类的构造需要一个handler形参。
package com.example.contentproviderdemo2.observer;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Message;
/**
* Created by 15426 on 2020/9/28.
*/
public class PersonObserver extends ContentObserver {
private Handler mHandler;//null
private static final int MESSAGE_WHAT = 100;
/**
* Creates a content observer.
*handler是用来运行onChange()
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
public PersonObserver(Handler handler) {
super(handler);
mHandler = handler;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//最简单的消息传递机制
Message message = Message.obtain();
message.what = MESSAGE_WHAT;
mHandler.sendMessage(message);
}
}
由于obeserver类的构造需要一个handler形参。
故在显示活动类中,写一个handler 字段,并且进行注册.
MainActivity.java
private static final int MESSAGE_WHAT = 100;
private Handler observerHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_WHAT:
initData();
break;
}
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//需要对ContentObserver进行注册
resolver = getContentResolver();
obsever = new PersonObserver(observerHandler);
resolver.registerContentObserver(PersonProvider.CONTENT_URI,true,obsever);
}