2023年3月15日(软件工程日报)
Android号称提供了4大组件,分别是活动Activity、广播Broadcast、服务Service和内容提供器
ContentProvider。其中内容提供器涵盖与内部数据存取有关的一系列组件,完整的内容组件由内容提
供器ContentProvider、内容解析器ContentResolver、内容观察器ContentObserver三部分组成。
ContentProvider给App存取内部数据提供了统一的外部接口,让不同的应用之间得以互相共享数据。像
上一章提到的SQLite可操作应用自身的内部数据库;上传和下载功能可操作后端服务器的文件;而
ContentProvider可操作当前设备其他应用的内部数据,它是一种中间层次的数据存储形式。
在实际编码中,ContentProvider只是服务端App存取数据的抽象类,开发者需要在其基础上实现一个完
整的内容提供器,并重写下列数据库管理方法。
onCreate:创建数据库并获得数据库连接。
insert:插入数据。
delete:删除数据。
update:更新数据。
query:查询数据,并返回结果集的游标。
getType:获取内容提供器支持的数据类型。这些方法看起来是不是很像SQLite?没错,ContentProvider作为中间接口,本身并不直接保存数据,
而是通过SQLiteOpenHelper与SQLiteDatabase间接操作底层的数据库。所以要想使用
ContentProvider,首先得实现SQLite的数据库帮助器,然后由ContentProvider封装对外的接口。以封
装用户信息为例,具体步骤主要分成以下3步。
1.编写用户信息表的数据库帮助器
这个数据库帮助器就是常规的SQLite操作代码,实现过程参见上一章的“6.2.3 数据库帮助器
SQLiteOpenHelper”,完整代码参见
chapter07\src\main\java\com\example\chapter07\database\UserDBHelper.java。
2.编写内容提供器的基础字段类
该类需要实现接口BaseColumns,同时加入几个常量定义。详细代码示例如下:
(完整代码见chapter07\src\main\java\com\example\chapter07\provider\UserInfoContent.java)
public class UserInfoContent implements BaseColumns {
// 这里的名称必须与AndroidManifest.xml里的android:authorities保持一致
public static final String AUTHORITIES =
"com.example.chapter07.provider.UserInfoProvider";
// 内容提供器的外部表名
public static final String TABLE_NAME = UserDBHelper.TABLE_NAME;
// 访问内容提供器的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES +
"/user");
// 下面是该表的各个字段名称
public static final String USER_NAME = "name";
public static final String USER_AGE = "age";
public static final String USER_HEIGHT = "height";
public static final String USER_WEIGHT = "weight";
}
3.通过右键菜单创建内容提供器
右击App模块的包名目录,在弹出的右键菜单中依次选择New→Other→Content Provider,打开如图7-
1所示的组件创建对话框。
在创建对话框的Class Name一栏填写内容提供器的名称,比如UserInfoProvider;在URI Authorities一
栏填写URI的授权串,比如“com.example.chapter07.provider.UserInfoProvider”;然后单击对话框右
下角的Finish按钮,完成提供器的创建操作。
上述创建过程会自动修改App模块的两处地方,一处是往AndroidManifest.xml添加内容提供器的注册
配置,配置信息示例如下:
<!-- provider的authorities属性值需要与Java代码的AUTHORITIES保持一致 -->
<provider
android:name=".provider.UserInfoProvider"
android:authorities="com.example.chapter07.provider.UserInfoProvider"
android:enabled="true"
android:exported="true" />
另一处是在包名目录下生成名为UserInfoProvider.java的代码文件,打开一看发现该类继承了
ContentProvider,并且提示重写onCreate、insert、delete、query、update、getType等方法,以便
对数据进行增删改查等操作。这个提供器代码显然只有一个框架,还需补充详细的实现代码,为此重写
onCreate方法,在此获取用户信息表的数据库帮助器实例,其他insert、delete、query等方法也要加入
对应的数据库操作代码,修改之后的内容提供器代码如下所示:
(完整代码见chapter07\src\main\java\com\example\chapter07\provider\UserInfoProvider.java)
public class UserInfoProvider extends ContentProvider {
private final static String TAG = "UserInfoProvider";
private UserDBHelper userDB; // 声明一个用户数据库的帮助器对象
public static final int USER_INFO = 1; // Uri匹配时的代号
public static final UriMatcher uriMatcher = new
UriMatcher(UriMatcher.NO_MATCH);static { // 往Uri匹配器中添加指定的数据路径
uriMatcher.addURI(UserInfoContent.AUTHORITIES, "/user", USER_INFO);
}
// 创建ContentProvider时调用,可在此获取具体的数据库帮助器实例
@Override
public boolean onCreate() {
userDB = UserDBHelper.getInstance(getContext(), 1);
return true;
}
// 插入数据
@Override
public Uri insert(Uri uri, ContentValues values) {
if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表
// 获取SQLite数据库的写连接
SQLiteDatabase db = userDB.getWritableDatabase();
// 向指定的表插入数据,返回记录的行号
long rowId = db.insert(UserInfoContent.TABLE_NAME, null, values);
if (rowId > 0) { // 判断插入是否执行成功
// 如果添加成功,就利用新记录的行号生成新的地址
Uri newUri =
ContentUris.withAppendedId(UserInfoContent.CONTENT_URI, rowId);
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(newUri, null);
}
db.close(); // 关闭SQLite数据库连接
}
return uri;
}
// 根据指定条件删除数据
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表
// 获取SQLite数据库的写连接
SQLiteDatabase db = userDB.getWritableDatabase();
// 执行SQLite的删除操作,并返回删除记录的数目
count = db.delete(UserInfoContent.TABLE_NAME, selection,
selectionArgs);
db.close(); // 关闭SQLite数据库连接
}
return count;
}
// 根据指定条件查询数据库
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表
// 获取SQLite数据库的读连接
SQLiteDatabase db = userDB.getReadableDatabase();
// 执行SQLite的查询操作
cursor = db.query(UserInfoContent.TABLE_NAME,
projection, selection, selectionArgs, null, null,
sortOrder);经过以上3个步骤之后,便完成了服务端App的接口封装工作,接下来再由其他App去访问服务端App的
数据
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性