讲ContentProvider之前,先介绍下SQLite以及SQListeOpenHelper
(1)SQLite介绍
SQLite是一个轻量级的数据库,不是一个C/S结构的数据库引擎,而是被集成到用户程序中。这个库也被动态链接,应用程序中直接调用相关API来使用SQLite功能,比跨进程通通信更有效率。SQLite将整个数据库作为一个单独的、可跨平台使用的文件存储在主机中
SQLite 是在世界上最广泛部署的 SQL 数据库引擎。像Android、IOS等移动操作系统中的数据库实现都是用SQLite
优点:
- 无服务器、零配置、事务性。
- 存储在一个单一磁盘文件中的一个完整数据库
- 数据库文件可以在不同字节顺序的机器自由共享
- 支持数据库大小2TB
- 足够小,全部源代码250KB
- 对数据操作快
- 开源
结构
image.png1.接口(Interface)
接口由SQLite C API组成,也就是说不管是程序、脚本语言还是库文件,最终都是通过它与SQLite交互的(我们通常用得较多的ODBC/JDBC最后也会转化为相应C API的调用)。
2.编译器(Compiler)
在编译器中,分词器(Tokenizer)和分析器(Parser)对SQL进行语法检查,然后把它转化为底层能更方便处理的分层的数据结构---语法树,然后把语法树传给代码生成器(code generator)进行处理。而代码生成器根据它生成一种针对SQLite的汇编代码,最后由虚拟机(Virtual Machine)执行。
3.虚拟机(Virtual Machine)
架构中最核心的部分是虚拟机,或者叫做虚拟数据库引擎(Virtual Database Engine,VDBE)。它和Java虚拟机相似,解释执行字节代码。VDBE的字节代码由128个操作码(opcodes)构成,它们主要集中在数据库操作。它的每一条指令都用来完成特定的数据库操作(比如打开一个表的游标)或者为这些操作栈空间的准备(比如压入参数)。总之,所有的这些指令都是为了满足SQL命令的要求(关于VM,后面会做详细介绍)。
4.后端(Back-End)
后端由B-树(B-tree),页缓存(page cache,pager)和操作系统接口(即系统调用)构成。B-tree和page cache共同对数据进行管理。B-tree的主要功能就是索引,它维护着各个页面之间的复杂的关系,便于快速找到所需数据。而pager的主要作用就是通过OS接口在B-tree和Disk之间传递页面。
参考:
(2)SQL语言学习
参考:
(3)SQListeOpenHelper
是一个sqlite辅助操作类
常用方法:
/**
* 创建数据库
*/
// 1. 创建 or 打开 可读/写的数据库(通过 返回的SQLiteDatabase对象 进行操作)
getWritableDatabase()
// 2. 创建 or 打开 可读的数据库(通过 返回的SQLiteDatabase对象 进行操作)
getReadableDatabase()
// 3. 数据库第1次创建时 则会调用,即 第1次调用 getWritableDatabase() / getReadableDatabase()时调用
// 在继承SQLiteOpenHelper类的子类中复写
onCreate(SQLiteDatabase db)
// 4. 数据库升级时自动调用
// 在继承SQLiteOpenHelper类的子类中复写
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
// 5. 关闭数据库
close()
/**
* 数据库操作(增、删、减、查)
*/
// 1. 查询数据
(Cursor) query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
// 查询指定的数据表返回一个带游标的数据集。
// 各参数说明:
// table:表名称
// colums:列名称数组
// selection:条件子句,相当于where
// selectionArgs:条件语句的参数数组
// groupBy:分组
// having:分组条件
// orderBy:排序类
// limit:分页查询的限制
// Cursor:返回值,相当于结果集ResultSet
(Cursor) rawQuery(String sql, String[] selectionArgs)
//运行一个预置的SQL语句,返回带游标的数据集(与上面的语句最大的区别 = 防止SQL注入)
// 2. 删除数据行
(int) delete(String table,String whereClause,String[] whereArgs)
// 3. 添加数据行
(long) insert(String table,String nullColumnHack,ContentValues values)
// 4. 更新数据行
(int) update(String table, ContentValues values, String whereClause, String[] whereArgs)
// 5. 执行一个SQL语句,可以是一个select or 其他sql语句
// 即 直接使用String类型传入sql语句 & 执行
(void) execSQL(String sql)
参考
ContentProvider
定义:内容提供者,是 Android 四大组件之一
作用:对外共享数据,使用ContentProvider的好处是统一了数据访问方式,实际上是对SQListOpenHelper的进一步封装,通过Uri映射来判断要操作数据库哪个表
(1)统一资源标识符(URI)
image.png- schema:固定为content://
- authority:ContentProvider的唯一标记,调用者可以通过这个找到它
- path:要操作的数据库表
- id:表中的某个记 录
// 设置URI
Uri uri = Uri.parse("content://com.carson.provider/User/1")
// 上述URI指向的资源是:名为 `com.carson.provider`的`ContentProvider` 中表名 为`User` 中的 `id`为1的数据
// 特别注意:URI模式存在匹配通配符* & #
// *:匹配任意长度的任何有效字符的字符串
// 以下的URI 表示 匹配provider的任何内容
content://com.example.app.provider/*
// #:匹配任意长度的数字字符的字符串
// 以下的URI 表示 匹配provider中的table表的所有行
content://com.example.app.provider/table/#
(2)ContentResolver
通过Uri来定位注册到系统的ContentProvider,找到ContentProvider之后通过ContentResolver来操作对应的数据库
获取方式:Context.getContentResolver()
常用方法:insert、query、delete、update等待
例如:读取联系人
(3)自定义ContentProvider
ContentProvider实际上是对SQLiteOpenHelper的进一步封装,通过Uri映射要判断选择需要操作的数据库中哪个表,并进行增删查改。
重写insert、query、update、delete、getType方法
public class MyProvider extends ContentProvider {
private SQLiteDatabase db;
public static final String AUTOHORITY = "cn.scu.myprovider";
public static final int User_Code = 1;
public static final int Job_Code = 2;
private static final UriMatcher mUriMatcher;
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 初始化
mUriMatcher.addURI(AUTOHORITY,"user",User_Code);
mUriMatcher.addURI(AUTOHORITY,"job",Job_Code);
// 若URI资源路径 = content://cn.scu.myprovider/user ,则返回注册码User_Code
// 若URI资源路径 = content://cn.scu.myprovider/job ,则返回注册码Job_Code
}
/**
* 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
*/
private String getTableName(Uri uri){
String tableName = null;
switch (mUriMatcher.match(uri)) {
case User_Code:
tableName = MySQListOpenHelper.USER_TABLE_NAME;
break;
case Job_Code:
tableName = MySQListOpenHelper.JOB_TABLE_NAME;
break;
}
return tableName;
}
@Override
public boolean onCreate() {
// 在ContentProvider创建时对数据库进行初始化
// 运行在主线程,故不能做耗时操作,此处仅作展示
SQLiteOpenHelper mDbHelper= new MySQListOpenHelper(getContext(),"test_person");
db = mDbHelper.getWritableDatabase();
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return db.query(getTableName(uri),projection,selection,selectionArgs,null,null,sortOrder,null);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
// 得到数据类型,即返回当前 Url 所代表数据的MIME类型
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
db.insert(getTableName(uri),null,values);
// 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
UriMatcher
作用:
- 在ContentProvider 中注册URI
- 根据 URI 匹配 ContentProvider 中对应的数据表
ContentObserver
定义:内容观察者
作用:观察 Uri引起 ContentProvider 中的数据变化 & 通知外界(即访问该数据访问者)
// 步骤1:注册内容观察者ContentObserver
getContentResolver().registerContentObserver(uri);
// 通过ContentResolver类进行注册,并指定需要观察的URI
// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("user", "userid", values);
getContext().getContentResolver().notifyChange(uri, null);
// 通知访问者
}
}
// 步骤3:解除观察者
getContentResolver().unregisterContentObserver(uri);
// 同样需要通过ContentResolver类进行解除
参考:
安卓开发进阶从小工到专家
网友评论