Android 中如果想将自己应用的数据(一般多为数据库中的数据)提供给第三发 app,那么我们只能通过 ContentProvider 来实现
ContentProvider 是应用程序之间共享数据的接口. 使用的时候首先自定义一个类继承 ContentProvider,然后覆写 query
、insert
、update
、delete
等方法. 最后别忘了作为 android 四大组件之一必须在 AndroidManifest 文件中进行注册
第三方 app 可以通过 ContentResolver 类来访问该 Provider
第三方 app 可以通过注册 ContentObserver 观测该Provider操作后的数据库变化
ContentProvider 屏蔽了数据存储的细节,内部实现对用户完全透明,用户只需要关心操作数据的 uri 就可以了,ContentProvider 可以实现不同 app 之间共享
Sql 也有增删改查的方法,但是 sql 只能查询本应用下的数据库. 而 ContentProvider 还可以去增删改查本地文件、.xml 文件的读取等
ContentProvider:内容提供者,用于对外提供数据
ContentResolver.notifyChange(uri)
:发出消息
ContentResolver:内容解析者,用于获取内容提供者提供的数据
ContentObserver:内容监听器,可以监听数据的改变状态
ContentResolver.registerContentObserver()
:监听消息
获取 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();
}
}
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;
}
}
<provider
android:name="cc.catface.provider.server.FileProvider"
android:authorities="cc.catface.provider.server"
android:enabled="true"
android:exported="true"
android:multiprocess="true" />
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();
定义并初始化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);
}