我正在将应用程序从LoaderManager
和Callbacks
迁移到使用ViewModel
和LiveData
的实现。我想继续使用现有的SQLiteDatabase
。
主要实现正常。 Activity
实例化ViewModel
并创建一个Observer
,如果它观察到View
中存在的MutableLiveData
中的变化,则更新ViewModel
。 ViewModel
使用SQLiteDatabase
通过查询从ContentProvider
获取数据(光标)。
但是我有其他活动可以更改数据库,而MainActivity
已停止但未被破坏。还有一个后台服务,可以在MainActivity
处于前台时对数据库进行更改。
其他活动和后台服务可以更改数据库中的值,因此可以影响MutableLiveData
中的ViewModel
。
我的问题是:如何观察SQLiteDatabase
中的变化以更新LiveData
?
这是MainActivity
的简化版本:
public class MainActivity extends AppCompatActivity {
private DrawerAdapter mDrawerAdapter;
HomeActivityViewModel homeActivityViewModel;
private Observer<Cursor> leftDrawerLiveDataObserver = new Observer<Cursor>() {
@Override
public void onChanged(@Nullable Cursor cursor) {
if (cursor != null && cursor.moveToFirst()) { // Do we have a non-empty cursor?
mDrawerAdapter.setCursor(cursor);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
homeActivityViewModel = ViewModelProviders.of(this).get(HomeActivityViewModel.class);
homeActivityViewModel.getLiveData().observe(this, leftDrawerLiveDataObserver);
homeActivityViewModel.updateLiveData(); //,LEFT_DRAWER_LIVEDATA_ID);
}
@Override
protected void onResume(){ // update the LiveData on Resume
super.onResume();
homeActivityViewModel.updateLiveData();
}
}
这是我的ViewModel
:
public class HomeActivityViewModel extends AndroidViewModel {
public HomeActivityViewModel(Application application) {
super(application);
}
@NonNull
private final MutableLiveData<Integer> updateCookie = new MutableLiveData<>();
@NonNull
private final LiveData<Cursor> cursorLeftDrawer =
Transformations.switchMap(updateCookie,
new Function<Integer, LiveData<Cursor>>() {
private QueryHandler mQueryHandler;
@Override
public LiveData<Cursor> apply(Integer input) {
mQueryHandler = new QueryHandler(getApplication().getContentResolver());
MutableLiveData<Cursor> cursorMutableLiveData = new MutableLiveData<>();
mQueryHandler.startQuery(ID, cursorMutableLiveData, URI,
new String[]{FeedData.ID, FeedData.URL},
null,null,null
);
return cursorMutableLiveData;
}
}
);
// By changing the value of the updateCookie, LiveData gets refreshed through the Observer.
void updateLiveData() {
Integer x = updateCookie.getValue();
int y = (x != null) ? Math.abs(x -1) : 1 ;
updateCookie.setValue(y);
}
@NonNull
LiveData<Cursor> getLiveData() {
return cursorLeftDrawer;
}
/**
* Inner class to perform a query on a background thread.
* When the query is completed, the result is handled in onQueryComplete
*/
private static class QueryHandler extends AsyncQueryHandler {
QueryHandler(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
MutableLiveData<Cursor> cursorMutableLiveData = (MutableLiveData<Cursor>) cookie;
cursorMutableLiveData.setValue(cursor);
}
}
}
答案 0 :(得分:1)
也许您应该看看Room
。 Room数据库在后台使用SQLite,并且在数据库中进行了任何更改后会自动通知您的LiveData
对象。因此,您无需担心查询和游标等。
看看这个tutorial!
答案 1 :(得分:1)
作为单例,这样的存储库将成为数据的“单一真相”。
public class MyRepository {
private static MyRepository sInstance;
private final Application mApp;
private MutableLiveData<MyDataType> mData;
// or MutableLiveData<List<MyDataType>> in case your cursor can have several rows
@NonNull
public static MyRepository getInstance(@NonNull Application app)
{
if(sInstance == null)
sInstance = new MyRepository(app);
return sInstance;
}
private MyRepository(@NonNull Application app)
{
mApp = app; // you will need it for getContentResolver()
}
public LiveData<MyDataType> getData() // or LiveData<List<MyDataType>>
{
if(mSourcesInfo != null)
return mSourcesInfo;
mData = new MutableLiveData<>();
// 1) use your AsyncQueryHandler to get your data as cursor
// 2) in onQueryComplete() convert the result cursor
// to MyDataType or List<MyDataType>
// and use setValue or postValue for to change your LiveData
// we immidiately return empty mData, its not a problems
// because all your Activities has observers that will be fired
// after you setValue/postValue in the onQueryComplete
return mData;
}
public void setData(...)
{
// insert data to your database AND also change the mData with
// setValue and postValue, so every activity/service that listen on
// the changes will be able to use it
}
}