0

I'm using firebase realtimedatabase,firestore and a recyclerview to show my data, it is a menu to a restaurant. I'm using storefirebase to the images and realtimedatabase to others informations like name,price and description. As i'm using MVVM, i have a viewModel and a repository too. Evrything is working fine but when i whant to the frist time show my data it pnly apper when i go to another fragment and go back.Here is my fragment code:


    HomeFragment

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        DataRetriever dataRetriever = new DataRetriever();
        HamburgerViewModel hamburgerViewModel;
        HamburgerViewModel.Factory factory = new HamburgerViewModel.Factory(dataRetriever);
        hamburgerViewModel = new ViewModelProvider(this,factory).get(HamburgerViewModel.class);

        RecyclerView recyclerView = getView().findViewById(R.id.recycle_view);
        ProductsAdapter productsAdapter = new ProductsAdapter(getContext(),"burger");
        recyclerView.setLayoutManager(new 
        GridLayoutManager(getContext(),2,GridLayoutManager.HORIZONTAL,false));
        recyclerView.setAdapter(productsAdapter);

        hamburgerViewModel.getProductsMutableLiveData().observe(getViewLifecycleOwner(),productsAdapter::update);
    } 

I'm using the DataRetriever as a way to retrieve data from firebase genetically to reuse the code in others repositories.

class DataRetriever

 public class DataRetriever {

    private final List<Product> productList;

    public DataRetriever(){

        productList = new ArrayList<>(0);
    }

    public List<Product> get(String child, String path){
 
FirebaseDatabase.getInstance().getReference(path).child(child).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {

                try {
                    productList.clear();


                    for (DataSnapshot i: snapshot.getChildren()) {

                        Product product = i.getValue(Product.class);
                        productList.add(product);

                    }
                }catch (NullPointerException e){

                    Log.e("firebase","Error getting the data, because of null elements", e);
                    productList.clear();
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

                Log.e("firebase","Error getting the data", error.toException());
            }
        });
        return productList;
    }
}

here is my viewModel and Repository:

    public class HamburgerViewModel extends ViewModel {

    private final MutableLiveData<List<Product>> liveData;

    public HamburgerViewModel(DataRetriever dataRetriever) {

        String child = "burger";
        ProductsRepository productsRepository = new ProductsRepository(child, dataRetriever);
        liveData = new MutableLiveData<List<Product>>();
        productsRepository.getAllProducts(liveData);
    }

    public MutableLiveData<List<Product>> getProductsMutableLiveData(){
        return liveData;
    }

public static class Factory implements ViewModelProvider.Factory{

        private final DataRetriever dataRetriever;

    public Factory(DataRetriever dataRetriever) {
        this.dataRetriever = dataRetriever;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        return (T) new HamburgerViewModel(dataRetriever);
    }
}
}

    private final List<Product> productList;

    public ProductsRepository(String child, DataRetriever dataRetriever) {
       productList = fetch(dataRetriever.get(child, "menu"));
    }

    public void getAllProducts(MutableLiveData<List<Product>> liveData){
        liveData.postValue(productList);
    }

    public void getAllProductsData(MutableLiveData<List<Product>> liveData){

        liveData.postValue(productList);
    }

    public List<Product> fetch(List<Product> productListFetched){

        productListFetched.removeIf(product -> product.getName() == null || product.getId() == null || product.getPrice() == 0 || product.getDescription() == null);

        return  productListFetched;
    }
}

Adpter


    private final List<Product> productList;
    private final Context context;
    private final String imagesPath;
    private final StorageReference storageReference;

    public ProductsAdapter(Context context, String imagesPath) {

        productList = new ArrayList<>(0);
        this.context = context;
        this.imagesPath = imagesPath;
        storageReference = FirebaseStorage.getInstance().getReference();
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_row,parent,false);

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

        holder.name.setText(productList.get(position).getName());
        holder.price.setText(String.valueOf(productList.get(position).getPrice()));
        GlideApp.with(context).load(storageReference.child(imagesPath+"/"+productList.get(position).getName()+".jpg")).into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return productList.size();
    }

    public void update(final List<Product> productList){

        this.productList.clear();
        this.productList.addAll(productList);

        notifyDataSetChanged();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {

        private final TextView price;
        private final TextView name;
        private final ImageView imageView;

        public ViewHolder(View view) {
            super(view);

            price = view.findViewById(R.id.price);
            name = view.findViewById(R.id.name);
            imageView = view.findViewById(R.id.imageProductView);
        }
    }
}

While i was trying to figue out what is happenig, i noticed that there is not problem in retriever the data, but because as firebase recover data asynchronously an my notifyDataSetChanged() in update on observer notify beforethe data is ready in my productList. And i have no problem with this. But when my data is ready my, observer don't update my new data till i go to another fragment and go back. So, my queistion is: how i deal with this problem or what is causing it? Is a sycronization problem? architectural problem? Bad implementation? Any help will be great, code, article, docs etc. Thanks!

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 1
    There is no way you can return the `productList` as a result of a method. Firebase API is asynchronous. So please check the duplicate to see how can you solve this using a callback. You might also be interested in reading this [resource](https://medium.com/firebase-tips-tricks/how-to-read-data-from-firebase-realtime-database-using-get-269ef3e179c5). – Alex Mamo Oct 07 '22 at 05:50
  • @AlexMamo Thanks a lot it was not just a help, it increased my skills as developer. – igor soares Oct 26 '22 at 13:32
  • Good to hear that, Igor ;) – Alex Mamo Oct 26 '22 at 13:45

1 Answers1

0

After you change the list that your adapter shows on the list view or recycler view, you need to call notifyDataSetChanged() on the adapter to tell it about the change.

For more on this see the documentation of RecyclerView.Adapter:: notifyDataSetChanged.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807