四大组件之一的内容提供者,我们虽然都是知道的,但是还是有很多小伙伴不是特别会用这个东西。我在这里写下这个博客,方便大家用到的时候进行查阅。
内容提供器ContentProvider
1.组成
内容提供器是跟数据存取有关的组件,完整的内容组件由内容提供器ContentProvider,内容解析器ContentResolver,内容观察器ContentObserver这三部分组成。
2.作用
ContentProvider为APP存取内部数据提供统一的外部接口,让不同的应用之间得以共享数据。
3.ContentProvider接口提供的接口方法
ContentProvider只是一个服务端的数据存取接口,开发者需要在其基础上实现一个具体类,并重写一下相关数据库管理方法。
- onCreate:创建数据库并获得数据库连接。
- query:查询数据。
- insert:插入数据。
- update:更新数据。
- delete:删除数据。
- getType:获取数据类型。
4.使用ContentProvider前提
要想使用ContentProvider,首先得实现SQLite的数据表帮助类,然后由ContentProvider封装对外接口。
5.代码示例
首先我们定义一个实体类
public class UserInfo {
private long rowid;
private int xuhao;
private String name;
private int age;
private long height;
private float weight;
private boolean married;
private String update_time;
private String phone;
private String password;
public long getRowid() {
return rowid;
}
public void setRowid(long rowid) {
this.rowid = rowid;
}
public int getXuhao() {
return xuhao;
}
public void setXuhao(int xuhao) {
this.xuhao = xuhao;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public long getHeight() {
return height;
}
public void setHeight(long height) {
this.height = height;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
public String getUpdate_time() {
return update_time;
}
public void setUpdate_time(String update_time) {
this.update_time = update_time;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
其次我们要定义数据库的帮助类
public class UserDBHelper extends SQLiteOpenHelper {
private static final String TAG = "UserDBHelper";
private static final String DB_NAME = "user.db";
private static final int DB_VERSION = 1;
private static UserDBHelper mHelper = null;
private SQLiteDatabase mDB = null;
public static final String TABLE_NAME = "user_info";
private UserDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
private UserDBHelper(Context context, int version) {
super(context, DB_NAME, null, version);
}
/**
* 单例获取Helper对象
*
* @param context
* @param version
* @return
*/
public static UserDBHelper getInstance(Context context, int version) {
if (version > 0 && mHelper == null) {
mHelper = new UserDBHelper(context, version);
} else if (mHelper == null) {
mHelper = new UserDBHelper(context);
}
return mHelper;
}
/**
* 通过读链接获取SQLiteDatabase
*
* @return
*/
public SQLiteDatabase openReadLink() {
if (mDB == null || mDB.isOpen() != true) {
mDB = mHelper.getReadableDatabase();
}
return mDB;
}
/**
* 通过写链接获取SQLiteDatabase
*
* @return
*/
public SQLiteDatabase openWriteLink() {
if (mDB == null || mDB.isOpen() != true) {
mDB = mHelper.getWritableDatabase();
}
return mDB;
}
/**
* 关闭数据库
*/
public void closeLink() {
if (mDB != null && mDB.isOpen() == true) {
mDB.close();
mDB = null;
}
}
/**
* 获取数据库名称
*
* @return
*/
public String getDbName() {
if (mHelper != null) {
return mHelper.getDatabaseName();
} else {
return DB_NAME;
}
}
/**
* 只在第一次打开数据库时执行,再此可进行表结构创建的操作
*
* @param db
*/
@Override
public void onCreate(SQLiteDatabase db) {
String drop_sql = "DROP TABLE IF EXISTS " + TABLE_NAME + ";";
db.execSQL(drop_sql);
String create_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ "name VARCHAR NOT NULL," + "age INTEGER NOT NULL,"
+ "height LONG NOT NULL," + "weight FLOAT NOT NULL,"
+ "married INTEGER NOT NULL," + "update_time VARCHAR NOT NULL,"
+ "phone VARCHAR," + "password VARCHAR" + ");";
db.execSQL(create_sql);
}
/**
* 在数据库版本升高时执行,可以根据新旧版本号进行表结构变更处理
*
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//其实这里是根据oldVersion的不同进行字段的添加
if (oldVersion == 1) {
// TODO: 2018/11/20 这里一会儿进行测试,先创建的表没有phone和password字段,然后添加
//Android 的 ALTER 命令不支持一次添加多列,只能分多次添加
String alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "phone VARCHAR;";
db.execSQL(alter_sql);
alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "password VARCHAR;";
db.execSQL(alter_sql);
}
}
/**
* 根据条件删除数据
*
* @param condition
* @return
*/
public int delete(String condition) {
int count = mDB.delete(TABLE_NAME, condition, null);
//null的位置,如果condition中用了?作为数值占位,比如"id>?",null要给new String[]{"2"}
return count;
}
/**
* 删除全部数据
*
* @return
*/
public int deleteAll() {
int count = mDB.delete(TABLE_NAME, "1=1", null);
return count;
}
/**
* 增加单条数据,其实就是包装了一下调用增加多条数据
*
* @param info
* @return
*/
public long insert(UserInfo info) {
ArrayList<UserInfo> infoArray = new ArrayList<>();
infoArray.add(info);
return insert(infoArray);
}
/**
* 根据条件进行查询
*
* @param condition
* @return
*/
public ArrayList<UserInfo> query(String condition) {
String sql = String.format("select rowid,_id,name,age,height,weight,married,update_time,phone,password from %s where %s;", TABLE_NAME, condition);
ArrayList<UserInfo> infoArray = new ArrayList<>();
Cursor cursor = mDB.rawQuery(sql, null);
if (cursor.moveToNext()) {
for (; ; cursor.moveToNext()) {
UserInfo info = new UserInfo();
info.setRowid(cursor.getLong(0));
info.setXuhao(cursor.getInt(1));
info.setName(cursor.getString(2));
info.setAge(cursor.getInt(3));
info.setHeight(cursor.getLong(4));
info.setWeight(cursor.getFloat(5));
//SQLite没有布尔型,用0表示false,用1表示true
info.setMarried(cursor.getInt(6) == 0 ? false : true);
info.setUpdate_time(cursor.getString(7));
info.setPhone(cursor.getString(8));
info.setPassword(cursor.getString(9));
infoArray.add(info);
if (cursor.isLast() == true) {
break;
}
}
}
cursor.close();
return infoArray;
}
/**
* 更新多条数据
*
* @param info
* @param condition
* @return
*/
public int update(UserInfo info, String condition) {
ContentValues cv = new ContentValues();
cv.put("name", info.getName());
cv.put("age", info.getAge());
cv.put("height", info.getHeight());
cv.put("weight", info.getWeight());
cv.put("married", info.isMarried());
cv.put("update_time", info.getUpdate_time());
cv.put("phone",info.getPhone());
cv.put("password",info.getPassword());
int count = mDB.update(TABLE_NAME, cv, condition, null);
return count;
}
/**
* 更新指定的数据条目
*
* @param info
* @return
*/
public int update(UserInfo info) {
return update(info, "rowid=" + info.getRowid());
}
/**
* 增加多条数据
*
* @param infoArray
* @return
*/
public long insert(ArrayList<UserInfo> infoArray) {
long result = -1;
//循环全部数据
for (int i = 0; i < infoArray.size(); i++) {
UserInfo info = infoArray.get(i);
ArrayList<UserInfo> tempArray = new ArrayList<>();
//如果存在同名记录,就更新记录。注意条件语句的等号后面要用单引号括起来
if (info.getName() != null && info.getName().length() > 0) {
String condition = String.format("name='%s'", info.getName());
tempArray = query(condition);
if (tempArray.size() > 0) {
update(info, condition);
result = tempArray.get(0).getRowid();
continue;
}
}
//如果不存在唯一性重复的记录,就插入新记录
ContentValues cv = new ContentValues();
cv.put("name", info.getName());
cv.put("age", info.getAge());
cv.put("height", info.getHeight());
cv.put("weight", info.getWeight());
cv.put("married", info.isMarried());
cv.put("update_time", info.getUpdate_time());
cv.put("phone",info.getPhone());
cv.put("password",info.getPassword());
result = mDB.insert(TABLE_NAME, "", cv);
//添加成功后返回行号,失败则返回-1
if (result == -1) {
return result;
}
}
return result;
}
}
然后是Provider代码
public class UserInfoProvider extends ContentProvider {
private UserDBHelper userDB;//声明一个用户数据库的帮助对象
public static final int USER_INFO = 1;//Uri匹配时的代号
public static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
//第一个参数要和清单文件到时候配置的一样。
//第二个是一个路径
//第三个是一个匹配代号
uriMatcher.addURI("com.hao.xxx.provider.UserInfoProvider","/user",USER_INFO);
}
@Override
public boolean onCreate() {
userDB = UserDBHelper.getInstance(getContext(),1);
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
Cursor cursor = null;
if (uriMatcher.match(uri) == USER_INFO){
//获取SQLite数据库的读连接
SQLiteDatabase db = userDB.getReadableDatabase();
//执行SQLite的查询操作
cursor = db.query(UserDBHelper.TABLE_NAME,projection,selection,selectionArgs,null,null,sortOrder);
//设置内容解析器监听
cursor.setNotificationUri(getContext().getContentResolver(),uri);
}
return cursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Uri newUri = uri;
if (uriMatcher.match(uri) == USER_INFO){
//获取SQLite数据库的写连接
SQLiteDatabase db = userDB.getWritableDatabase();
//向指定的表插入数据,返回记录的型号
long rowId = db.insert(UserDBHelper.TABLE_NAME,null,values);
if (rowId > 0){//判断插入是否执行成功
//如果添加成功,利用新记录的行号生成新的地址
newUri = ContentUris.withAppendedId(Uri.parse("content://com.hao.xxx.provider.UserInfoProvider/user"),rowId);
//通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(newUri,null);
}
db.close();
}
return uri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
int count = 0;
if (uriMatcher.match(uri) == USER_INFO){
//获取SQLite数据库的写连接
SQLiteDatabase db = userDB.getWritableDatabase();
//执行SQlite的删除操作,返回删除记录的数目
count = db.delete(UserDBHelper.TABLE_NAME,selection,selectionArgs);
db.close();
}
return count;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
接着我们是对Provider的配置
在清单文件application节点下
<provider
android:authorities="com.hao.xxx.provider.UserInfoProvider"
android:name=".UserInfoProvider"
android:enabled="true"
android:exported="true"/>
我们可以清楚的看到,其实Provider中就是将已经按照数据库规则组装好的数据添加到数据库中,或对数据库进行相应的操作。其实就是对外开放的数据库操作接口。
接下来我们聊聊内容解析器
内容解析器ContentResolver
1.ContentResolver对象获取方法
要获取ContentResolver对象,在Activity代码中调用getContentResolver方法即可。
2.ContentResolver提供方法
ContentResolver提供的方法与ContentProvider是一一对应的,比如query,insert,update,delete,getType等方法,连方法的参数类型都一模一样。
3.query方法各参数说明
- uri:Uri类型,可以理解为本次操作的数据表路径。
- projection:字符串数组类型,指定将要查询的字段名称列表。
- selection:字符串类型,指定查询条件。
- selectionArgs:字符串数组类型,指定查询条件中的参数取值列表。
- sortOrder:字符串类型,指定排序条件。
4.代码示例
插入数据
private void addUser(ContentResolver resolver,UserInfo userInfo){
ContentValues name = new ContentValues();
name.put("name",userInfo.getName());
name.put("age",userInfo.getName());
name.put("height",userInfo.getName());
name.put("weight",userInfo.getName());
name.put("married",userInfo.getName());
name.put("update_time",userInfo.getName());
//通过内容解析器往指定Uri中添加用户信息
resolver.insert(Uri.parse("content://com.hao.xxx.provider.UserInfoProvider/user"),name);
}
读取数据
private ArrayList<UserInfo> readAllUser(ContentResolver resolver){
ArrayList<UserInfo> userArray = new ArrayList<>();
//通过内容解析器从指定Uri中获取用户记录的游标
Cursor cursor = resolver.query(Uri.parse("content://com.hao.xxx.provider.UserInfoProvider/user"),null,null,null,null);
//循环取出游标指向的每条用户记录
while (cursor.moveToNext()){
UserInfo user = new UserInfo();
user.setName(cursor.getString(1));
user.setAge(cursor.getInt(2));
user.setHeight(cursor.getLong(3));
user.setWeight(cursor.getFloat(4));
user.setMarried(cursor.getInt(5)==0?false:true);
user.setUpdate_time(cursor.getString(6));
userArray.add(user);
}
cursor.close();
return userArray;
}
调用方法时
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bt_write:
//插入数据
UserInfo userInfo = new UserInfo();
userInfo.setXuhao(10);
userInfo.setName("小红");
userInfo.setUpdate_time("2018-11-24");
userInfo.setMarried(false);
userInfo.setAge(99);
userInfo.setWeight(100);
userInfo.setHeight(180);
addUser(getContentResolver(),userInfo);
break;
case R.id.bt_read:
//读取数据
Toast.makeText(this,readAllUser(getContentResolver()).size()+"",Toast.LENGTH_SHORT).show();
break;
}
}
这样我们就可以将自己的数据库中某一张表开放出去,让其他手机中的应用读取里面的数据了。