Loader → Flow + ViewModel
老写法(Java)
public class MyActivity extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
private SimpleCursorAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter = new SimpleCursorAdapter(this, R.layout.item_row, null,
new String[]{"name"}, new int[]{R.id.tv_name}, 0);
getLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this, MyContentProvider.CONTENT_URI,
null, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}
问题在哪里
Loader API 在 API 28 已废弃。需要 Activity 实现 LoaderCallbacks 接口,代码分散在三个回调和初始化之间。基于 Cursor,如果不用 ContentProvider 就用不了,使用场景极窄。
新写法(Room + Flow + ViewModel)
DAO:
@Dao
interface ItemDao {
@Query("SELECT * FROM items ORDER BY name ASC")
fun getAllItems(): Flow<List<Item>>
}
ViewModel:
class ItemViewModel(private val dao: ItemDao) : ViewModel() {
val items: LiveData<List<Item>> = dao.getAllItems().asLiveData()
}
Activity / Fragment:
viewModel.items.observe(viewLifecycleOwner) { list ->
adapter.submitList(list)
}
一句话注意
Flow.asLiveData() 默认在 Dispatchers.Main 上收集,不需要手动切线程。但 Room 的 Flow 查询需要在数据库线程池上执行,Room 内部已经帮你处理了线程切换,不需要再用 flowOn。
如果不需要 LiveData(比如配合 Compose),直接收集 Flow 也行:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
dao.getAllItems().collect { list ->
adapter.submitList(list)
}
}
}
repeatOnLifecycle 确保只在 STARTED 以上状态收集,切换到后台自动取消,避免资源浪费。用 lifecycleScope.launch 直接 collect 的话要特别注意这一点,否则可能泄漏。
Java Android 老项目迁移系列,持续更新中。