简体   繁体   中英

Retrieving Data from FireStore using MVVM Architecture

I am trying to follow Android Architecture principles and would like you implement them on top of my FireStore database.

Currently I have a repository Class that handles all my queries with the underlying data. I have a Fragment that requires a Set<String> of keys from the fields in a document and am wondering what the best approach to retrieve this data is. In my previous question Alex Mamo suggested using an Interface in conjunction with an onCompleteListener since retrieval of data from Firestore is Asynchronous .

This approach seems to work but I am unsure of how to extract the data from this Interface to a variable local to my Fragment . If I wish to use this data would my code have to be within my definition of the abstract method?

Am I still following MVVM principle if to get the data from Firestore to my Fragment I have to pass an Interface object defined in a Fragment as a parameter to my repository?

Is this the recommended approach for querying a Firestore database using a Repository?

Below is my Interface and method that calls on a ViewModel to retrieve data:

public interface FirestoreCallBack{
    void onCallBack(Set<String> keySet);
}

public void testMethod(){
    Log.i(TAG,"Inside testMethod.");
    mData.getGroups(new FirestoreCallBack() {
    //Do I have to define what I want to use the data for here (e.g. display the contents of the set in a textview)?
        @Override
        public void onCallBack(Set<String> keySet) {
            Log.i(TAG,"Inside testMethod of our Fragment and retrieved: " + keySet);
            myKeySet = keySet;
            Toast.makeText(getContext(),"Retrieved from interface: "+ myKeySet,Toast.LENGTH_SHORT).show();
        }
    });
}

My ViewModel method to call on the Repository:

private FirebaseRepository mRepository;
public void getGroups(TestGroupGetFragment.FirestoreCallBack firestoreCallBack){
    Log.i(TAG,"Inside getGroups method of FirebaseUserViewModel");
    mRepository.getGroups(firestoreCallBack);
}

Finally my Repository method to query my FireStore database:

public void getGroups(final TestGroupGetFragment.FirestoreCallBack firestoreCallBack){
    Log.i(TAG,"Attempting to retrieve a user's groups.");
    userCollection.document(currentUser.getUid()).get().addOnCompleteListener(
            new OnCompleteListener<DocumentSnapshot>() {
                @Override
                public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                    if (task.isSuccessful()){
                        DocumentSnapshot document = task.getResult();
                        Log.i(TAG,"Success inside the onComplete method of our document .get() and retrieved: "+ document.getData().keySet());
                        firestoreCallBack.onCallBack(document.getData().keySet());
                    } else {
                        Log.d(TAG,"The .get() failed for document: " + currentUser.getUid(), task.getException());
                    }
                }
            });
    Log.i(TAG, "Added onCompleteListener to our document.");
}

EDITED

public void testMethod(){
    Log.i(TAG,"Inside testMethod.");
    mData.getGroups(new FirestoreCallBack() {
        @Override
        public void onCallBack(Set<String> keySet) {
            Log.i(TAG,"Inside testMethod of our Fragment and retrieved: " + keySet);
            myKeySet = keySet;
            someOtherMethod(myKeySet); //I know I can simply pass keySet.
            Toast.makeText(getContext(),"GOT THESE FOR YOU: "+ myKeySet,Toast.LENGTH_SHORT).show();
        }
    });

    Log.i(TAG,"In testMethod, retrieving the keySet returned: "+ myKeySet);
}

Instead of interface I only use LiveData to bring the data to a recyclerview, for example.

First, We have to create our Firestore query . In this example, I am listing all documents inside a collection.

public class FirestoreLiveData<T> extends LiveData<T> {

    public static final String TAG = "debinf firestore";

    private ListenerRegistration registration;

    private CollectionReference colRef;
    private Class clazz;

    public FirestoreLiveData(CollectionReference colRef, Class clazz) {
        this.colRef = colRef;
        this.clazz = clazz;
    }


    EventListener<QuerySnapshot> eventListener = new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(@Nullable QuerySnapshot queryDocumentSnapshots, @Nullable FirebaseFirestoreException e) {
            if (e != null) {
                Log.i(TAG, "Listen failed.", e);
                return;
            }


            if (queryDocumentSnapshots != null && !queryDocumentSnapshots.isEmpty()) {
                List<T> itemList = new ArrayList<>();
                for (DocumentSnapshot snapshot : queryDocumentSnapshots.getDocuments()) {
                    T item = (T) snapshot.toObject(clazz);
                    itemList.add(item);
                    Log.i(TAG, "snapshot is "+snapshot.getId());
                }
                setValue((T) itemList);
            }
        }
    };

    @Override
    protected void onActive() {
        super.onActive();
        registration = colRef.addSnapshotListener(eventListener);
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        if (!hasActiveObservers()) {
            registration.remove();
            registration = null;
        }
    }
}

Next, we create a link in our Repository

public class Repository {

    public Repository() {
    }

    public LiveData<List<ProductsObject>> productListening(GroupObject group) {
        return new FirestoreLiveData<>(DatabaseRouter.getCollectionRef(group.getGroupCreator()).document(group.getGroupKey()).collection("ProductList"), ProductsObject.class);
    }

}

After that, we create our ViewModel :

public class MyViewModel extends ViewModel {

    Repository repository = new Repository();

    public LiveData<List<ProductsObject>> getProductList(GroupObject groupObject) {
        return repository.productListening(groupObject);
    }

}

And finally, in our MainActivity or Fragment we observe the data contained in ou Firestore:

    viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
    viewModel.getProductList(groupObject).observe(this, new Observer<List<ProductsObject>>() {
        @Override
        public void onChanged(@Nullable List<ProductsObject> productsObjects) {
            //Log.i(TAG, "viewModel: productsObjects is "+productsObjects.get(0).getCode());
            adapter.submitList(productsObjects);
        }
    });

I hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM