ContentProvider → Room + Repository
老写法(Java + ContentProvider)
java
// 查询联系人(系统 ContentProvider)
Cursor cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
while (cursor.moveToNext()) {
String name = cursor.getString(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
}
cursor.close();
自定义 ContentProvider:
java
public class ItemProvider extends ContentProvider {
private MyDbHelper dbHelper;
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
return db.rawQuery("SELECT * FROM item", null);
}
// ... insert、update、delete 同样要重写
}
问题在哪里
自定义 ContentProvider 太重了------要实现 CRUD 全部六个方法,大多数项目只用其中一两个。一个简单的数据查询要通过 URI 解析、权限检查、跨进程调用,复杂度远超它提供的价值。
系统 ContentProvider(如联系人、日历)该用还得用,但自己项目里的数据没必要包装成 ContentProvider。
新写法(Room + Repository)
kotlin
// DAO
@Dao
interface ItemDao {
@Query("SELECT * FROM item")
fun getAll(): Flow<List<Item>>
@Insert
suspend fun insert(item: Item)
@Delete
suspend fun delete(item: Item)
}
// Repository
class ItemRepository(private val dao: ItemDao) {
fun getAll(): Flow<List<Item>> = dao.getAll()
suspend fun insert(item: Item) = dao.insert(item)
suspend fun delete(item: Item) = dao.delete(item)
}
// ViewModel
class ItemViewModel(private val repo: ItemRepository) : ViewModel() {
val items: LiveData<List<Item>> = repo.getAll().asLiveData()
fun insert(item: Item) {
viewModelScope.launch(Dispatchers.IO) { repo.insert(item) }
}
}
一句话注意
如果确实需要向其他应用暴露数据(比如你做了一个 Launcher 或输入法),ContentProvider 仍然是最佳选择。只是项目内部自己的数据读写,Room + Repository 就够了,没必要上一套 ContentProvider。
如果原来的 Provider 是被自己项目内部多个进程共享的,迁移时需要考虑 DataStore 多进程替代方案或用 MMKV(腾讯开源的高性能 KV 库)。
Java Android 老项目迁移系列,持续更新中。