Android Studio: Good practice for handling results from addGeoQueryDataEventListener

梦想的初衷 提交于 2020-01-16 14:09:59

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!