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!