问题
I am using Android Architecture Components (Model-View-ViewModel) to get the results from geoQuery.addGeoQueryDataEventListener()
from GeoFirestore
implementation 'com.github.imperiumlabs:GeoFirestore-Android:v1.5.0'
Each event has its own callback and I am interested in all of them.
I am also using BottomNavigationView
what means that I only have one activity and all my logic is placed in the fragments.
I started off by implementing onDocumentEntered()
as shown below and I realized that when I navigate to next activity and get back to the previous one where MVVM is been called, the recyclerView momentaneously duplicates the data.
public class FirestoreGeoQuery extends LiveData<List<StoreModel>> {
private static final String TAG = "debinf FBGeoQuery";
private GeoQuery geoQuery;
private Class clazz;
private List<StoreModel> itemList = new ArrayList<>();
public FirestoreGeoQuery(GeoQuery geoQuery, Class clazz) {
this.geoQuery = geoQuery;
this.clazz = clazz;
}
GeoQueryDataEventListener geoQueryDataEventListener = new GeoQueryDataEventListener() {
@Override
public void onDocumentEntered(@NotNull DocumentSnapshot documentSnapshot, @NotNull GeoPoint geoPoint) {
Log.i(TAG, "onDocumentEntered: "+documentSnapshot);
if (documentSnapshot.exists()) {
StoreModel item = (StoreModel) documentSnapshot.toObject(clazz);
item.setGeoPoint(geoPoint);
Log.i(TAG, "onDocumentEntered: addGeoQueryDataEventListener - store.name: "+item.getName()+", address: "+item.getAddress()+", geoPoint: "+item.getGeoPoint());
itemList.add(item);
}
}
@Override
public void onDocumentExited(@NotNull DocumentSnapshot documentSnapshot) {
Log.i(TAG, "onDocumentExited: ");
}
@Override
public void onDocumentMoved(@NotNull DocumentSnapshot documentSnapshot, @NotNull GeoPoint geoPoint) {
Log.i(TAG, "onDocumentMoved: ");
}
@Override
public void onDocumentChanged(@NotNull DocumentSnapshot documentSnapshot, @NotNull GeoPoint geoPoint) {
Log.i(TAG, "onDocumentChanged: ");
}
@Override
public void onGeoQueryReady() {
Log.i(TAG, "onGeoQueryReady: ");
setValue(itemList);
}
@Override
public void onGeoQueryError(@NotNull Exception e) {
Log.i(TAG, "onGeoQueryError: ");
}
};
@Override
protected void onActive() {
super.onActive();
geoQuery.addGeoQueryDataEventListener(geoQueryDataEventListener);
}
@Override
protected void onInactive() {
super.onInactive();
if (!hasActiveObservers()) {
geoQuery.removeGeoQueryEventListener(geoQueryDataEventListener);
}
}
}
So my question is: How to properly handle the results from each event callback?
I appreciate any help!
回答1:
You can setup a custom class to wrap the data and the status of the event listener and observe it within a LiveData
. You can also handle the listener in the ViewModel
itself so that it's only added once, and cleared once the ViewModel
is cleared. Storing data in your ViewModel
is also a good idea if you want it to persist throughout the lifecycle of your Activity
.
ViewModel
private MutableLiveData<Resource<StoreModel>> data;
private Resource resource;
private List<StoreModel> itemList = new ArrayList<>();
public MyViewModel() {
resource = new Resource();
data = getData();
GeoQueryDataEventListener geoQueryDataEventListener = new GeoQueryDataEventListener() {
@Override
public void onDocumentEntered(@NotNull DocumentSnapshot documentSnapshot, @NotNull GeoPoint geoPoint) {
Log.i(TAG, "onDocumentEntered: "+documentSnapshot);
if (documentSnapshot.exists()) {
StoreModel item = (StoreModel) documentSnapshot.toObject(clazz);
item.setGeoPoint(geoPoint);
data.postValue(resource.onDocumentEntered(item));
}
}
@Override
public void onDocumentExited(@NotNull DocumentSnapshot documentSnapshot) {
Log.i(TAG, "onDocumentExited: ");
data.postValue(resource.onDocumentExited(...));
}
@Override
public void onDocumentMoved(@NotNull DocumentSnapshot documentSnapshot, @NotNull GeoPoint geoPoint) {
Log.i(TAG, "onDocumentMoved: ");
data.postValue(resource.onDocumentMoved(...));
}
@Override
public void onDocumentChanged(@NotNull DocumentSnapshot documentSnapshot, @NotNull GeoPoint geoPoint) {
Log.i(TAG, "onDocumentChanged: ");
data.postValue(resource.onDocumentChanged(...));
}
@Override
public void onGeoQueryReady() {
Log.i(TAG, "onGeoQueryReady: ");
data.postValue(resource.onGeoQueryReady(...));
}
@Override
public void onGeoQueryError(@NotNull Exception e) {
Log.i(TAG, "onGeoQueryError: ");
data.postValue(resource.onGeoQueryError(...));
}
geoQuery.addGeoQueryDataEventListener(geoQueryDataEventListener);
}
@Override
protected void onCleared() {
geoQuery.removeGeoQueryEventListener(geoQueryDataEventListener);
}
public MutableLiveData<Resource<StoreModel>> getData() {
if (data == null) {
data = new MutableLiveData<Resource<StoreModel>>();
}
return data;
}
Resource
class Resource {
private Status currentStatus;
private StoreModel item;
public Resource() { }
public Resource onDocumentEntered(StoreModel item) {
this.status = Status.ON_DOCUMENT_ENTERED;
this.item = item;
return this;
}
.... handle other functions
}
Status
enum Status {
ON_DOCUMENT_ENTERED,
ON_DOCUMENT_EXITED,
....
}
And you can initialize & observe it in your Fragment as follows:
@Override
protected void onStart() {
// initialize existing data
if (viewModel.getItemList().size() > 0) {
updateUI(data);
}
// observe listener events
observer = new Observer<Resource<List<String>>>() {
@Override
public void onChanged(@Nullable final Resource<List<String>> resource) {
switch(resource.getCurrentStatus()) {
case Status.ON_DOCUMENT_ENTERED:
viewModel.getItemList().add(resource.getItem());
....
break;
case Status.ON_DOCUMENT_EXITED:
...
break;
...
}
}
};
viewModel.getData().observe(this, observer);
}
@Override
protected void onStop() {
viewModel.getData().removeObserver(observer);
}
来源:https://stackoverflow.com/questions/59120985/android-studio-good-practice-for-handling-results-from-addgeoquerydataeventlist