目录
- ContentProvider 如何实现数据共享
- 为什么要用 ContentProvider? 它和 sql 的实现上有什么差别?
- ContentProvider、ContentResolver、ContentObserver 之间的关系
- 如何访问 asserts 资源目录下的数据库?
- 如何在高并发下进行数据库查询
- 案例-查询联系人
- 案例-完整的使用
- 步骤一、server app中定义ContentProvider
- 步骤二、server app清单文件中注册
- 步骤三、client app调用provider
- client app中使用ContentObserver
- 数据实体
- 数据库操作
ContentProvider 如何实现数据共享
- Android 中如果想将自己应用的数据(一般多为数据库中的数据)提供给第三发 app,那么我们只能通过 ContentProvider 来实现
- ContentProvider 是应用程序之间共享数据的接口. 使用的时候首先自定义一个类继承 ContentProvider,然后覆写
query
、insert
、update
、delete
等方法. 最后别忘了作为 android 四大组件之一必须在 AndroidManifest 文件中进行注册 - 第三方 app 可以通过 ContentResolver 类来访问该 Provider
- 第三方 app 可以通过注册 ContentObserver 观测该Provider操作后的数据库变化
为什么要用 ContentProvider? 它和 sql 的实现上有什么差别?
- ContentProvider 屏蔽了数据存储的细节,内部实现对用户完全透明,用户只需要关心操作数据的 uri 就可以了,ContentProvider 可以实现不同 app 之间共享
- Sql 也有增删改查的方法,但是 sql 只能查询本应用下的数据库. 而 ContentProvider 还可以去增删改查本地文件、.xml 文件的读取等
ContentProvider、ContentResolver、ContentObserver 之间的关系
- ContentProvider:内容提供者,用于对外提供数据
-
ContentResolver.notifyChange(uri)
:发出消息 - ContentResolver:内容解析者,用于获取内容提供者提供的数据
- ContentObserver:内容监听器,可以监听数据的改变状态
-
ContentResolver.registerContentObserver()
:监听消息
如何访问 asserts 资源目录下的数据库?
- 获取 assert 目录下的 db 文件
AssetManager assetManager = getAssets();
InputStream is = assetManager.open("myuser.db");
// 将文件拷贝到 /data/data/cc.catface.android.asserts.sqlite/databases/myuser.db
// 如果 databases 目录不存在则创建
File file = new File("/data/data/cc.catface.android.asserts.sqlite/databases");
if (!file.exists()) {
file.mkdirs();
}
FileOutputStream fos = new FileOutputStream(new File(file, "myuser.db"));
byte[] buff = new byte[1024 * 8];
int len = -1;
while((len = is.read(buff)) != -1){
fos.write(buff, 0, len);
}
fos.close();
is.close();
- 访问数据库
SQLiteDatabase database = openOrCreateDatabase("myuser.db", MODE_PRIVATE, null);
String sql = "select c_name from t_user";
Cursor cursor = database.rawQuery(sql, null);
while(cursor.moveToNext()){
String string = cursor.getString(0);
Log.d("tag", string);
}
cursor.close();
database.close();
如何在高并发下进行数据库查询
不要关联多表查询,减少链接时间,创建索引、将查询到的数据采用缓存策略等等
案例-查询联系人
- 动态申请权限
- 清单文件中添加权限申明
<uses-permission android:name="android.permission.READ_CONTACTS" />
- 代码中动态申请权限
if (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, 1);
}
- 获取联系人
Cursor cursor = null;
try {
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.d("catface", "读取联系人:" + displayName + " || " + number);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
案例-完整的使用
步骤一、server app中定义ContentProvider
public class FileProvider extends ContentProvider {
private final String TAG = "catface";
private static final Pair<String, Integer> TABLE_TEXT = new Pair<>("table_text", 0);
private static final Pair<String, Integer> TABLE_AUDIO = new Pair<>("table_audio", 1);
private static final Pair<String, Integer> TABLE_VIDEO = new Pair<>("table_video", 2);
// 主机名
private static final String AUTHORITY = "cc.catface.provider.server";
private static final Uri HOST_URI = Uri.parse("content://" + AUTHORITY);
private static UriMatcher mUriMatcher;
// 本地匹配规则
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, TABLE_TEXT.first, TABLE_TEXT.second);
mUriMatcher.addURI(AUTHORITY, TABLE_AUDIO.first, TABLE_AUDIO.second);
mUriMatcher.addURI(AUTHORITY, TABLE_VIDEO.first, TABLE_VIDEO.second);
}
public FileProvider() {
Log.d(TAG, "constructor...");
}
@Override
public boolean onCreate() {
Log.d(TAG, "create...");
return true;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
int match = mUriMatcher.match(uri);
if (match == TABLE_TEXT.second) {
return TABLE_TEXT.first;
}
if (match == TABLE_AUDIO.second) {
return TABLE_AUDIO.first;
}
if (match == TABLE_VIDEO.second) {
return TABLE_VIDEO.first;
}
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
// Log.d(TAG, "insert-->uri: " + uri + " || values's size: " + values.size()); // 打印所有入参
String tableName = getType(uri);
if (null == getContext() || null == values || null == tableName) return null;
if (tableName.equals(TABLE_TEXT.first)) {
TextInfo info = new TextInfo();
if (values.containsKey("uuid")) info.setUuid(values.getAsString("uuid"));
if (values.containsKey("text")) info.setText(values.getAsString("text"));
if (values.containsKey("create_time")) info.setCreateTime(values.getAsLong("create_time"));
if (values.containsKey("search_count")) info.setSearchCount(values.getAsInteger("search_count"));
long rowId = DBHelper.getInstance().getTextInfoDAO().insert(info);
getContext().getContentResolver().notifyChange(HOST_URI, null);
return ContentUris.withAppendedId(uri, rowId);
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
// Log.d(TAG, "delete-->uri: " + uri + " || selection: " + selection + " || selectionArgs: " + Arrays.asList(selectionArgs).toString()); // 打印所有入参
getContext().getContentResolver().notifyChange(HOST_URI, null); // 数据库发生变化通知客户app的观察者
return DBHelper.getInstance().getTextInfoDAO().deleteByText(selectionArgs[0]);
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
// Log.d(TAG, "update-->uri: " + uri + " || values's size: " + values.size() + " || selection: " + selection + " || selectionArgs: " + Arrays.asList(selectionArgs).toString()); // 打印所有入参
int rowId = DBHelper.getInstance().getTextInfoDAO().updateText(selectionArgs[0], selectionArgs[1]);
getContext().getContentResolver().notifyChange(HOST_URI, null);
return rowId;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
// Log.d(TAG, "query-->uri: " + uri + " || projection[]: " + Arrays.asList(projection).toString() + " || selection: " + selection + " || selectionArgs[]: " + Arrays.asList(selectionArgs).toString() + " || sortOrder: " + sortOrder); // 打印所有入参
Cursor cursor = DBHelper.getInstance().getTextInfoDAO().select4CP(-1, 0);
cursor.setNotificationUri(getContext().getContentResolver(), HOST_URI);
return cursor;
}
}
步骤二、server app清单文件中注册
<provider
android:name="cc.catface.provider.server.FileProvider"
android:authorities="cc.catface.provider.server"
android:enabled="true"
android:exported="true"
android:multiprocess="true" />
步骤三、client app调用provider
- uri
Uri uri = Uri.parse("content://cc.catface.provider.server/table_text");
- 增
ContentValues values = new ContentValues();
values.put("uuid", UUID.randomUUID().toString());
values.put("text", binding.etText.getText().toString().trim());
values.put("create_time", System.currentTimeMillis());
values.put("search_count", Integer.valueOf(binding.etCount.getText().toString()));
Uri insertUri = getContentResolver().insert(uri, values);
Log.d("catface", "insert-->insertUri: " + insertUri.toString());
- 删
int rowId = getContentResolver().delete(uri, null, new String[]{binding.etDeleteText.getText().toString().trim()});
Log.d("catface", "delete-->rowId: " + rowId);
- 改
int rowId = getContentResolver().update(uri, new ContentValues(), null, new String[]{binding.etOldText.getText().toString().trim(), binding.etNewText.getText().toString().trim()});
Log.d("catface", "update-->rowId: " + rowId);
- 查
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (null == cursor) return;
while (cursor.moveToNext()) {
String text = cursor.getString(cursor.getColumnIndex("text"));
long create_time = cursor.getLong(cursor.getColumnIndex("create_time"));
Log.d("catface", "query: " + text + " || " + create_time);
}
cursor.close();
client app中使用ContentObserver
- 定义并初始化ContentObserver
class CustomObserver extends ContentObserver {
public CustomObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
Log.d("catface", "CustomObserver-->onChange-->uri: " + uri + " || selfChange: " + selfChange);
}
}
CustomObserver mProviderObserver = new CustomObserver(new Handler());
- 注册ContentObserver
getContentResolver().registerContentObserver(Uri.parse("content://cc.catface.provider.server/table_text"), true, mProviderObserver);
- 解注册ContentObserver
getContentResolver().unregisterContentObserver(mProviderObserver);
数据实体
@Entity(tableName = "text_info")
public class TextInfo {
@NonNull @PrimaryKey private String uuid;
private String text;
@ColumnInfo(name = "create_time") private long createTime;
@ColumnInfo(name = "search_count") private int searchCount;
// getter & setter
}
数据库操作
@Dao
public interface TextInfoDAO {
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insert(TextInfo info);
@Query("delete from text_info where text=:text")
int deleteByText(String text);
@Query("update text_info set text=:newText where text=:oldText")
int updateText(String oldText, String newText);
@Query("select * from text_info order by create_time desc limit :pageSize offset :pageSize * :page")
Cursor select4CP(int pageSize, int page);
}