Search code examples
javafirebasefirebase-realtime-databaseandroid-asynctaskasynccallback

How to populate ArrayList inside onDataChange , arraylist being used to update Adapter


I am stuck in a situation, I have to get populate ArrayList by FirebaseRealtimeDatabase, now this returns a ViewHolder, but since onDataChange is Asynchronous , a call to getItemSize() is made before onDataChange has completed data population, leading it to returns 0, and hence onCreateViewHoldwer is never called. How to make Adapter to wait until all data from onDataChange is fetched or else notify Adapter that it needs to refresh?? Note: This all is happening inside Adapter class.

I tried implementing a callback, but that callback is also called after getItemSize()

Here is minmized higlight of my code

public class SubjectAdapter extends RecyclerView.Adapter<SubjectHolder> {
public interface MyCallback{
    void onCallback(Subjects s);
}
//declarations


public SubjectAdapter(Context context) {
    //initialisations
    populateList(new MyCallback() {
        @Override
        public void onCallback(Subjects s) {
            subjectsArrayList.add(s);
            Log.e("callback",s.toString());
        }
    });
    mContext=context;
}

public void populateList(final MyCallback myCallback){
    final ArrayList<String> units=new ArrayList<String>();

    semesterInfoReference.child("1").addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for(DataSnapshot dsa:dataSnapshot.getChildren()) {
                    //Log.e("dsa",dsa.getValue().toString());
                    String subjectCode = dsa.getKey();
                    for(DataSnapshot ds:dsa.getChildren()) {
                        SubjectObj subjectObj = ds.getValue(SubjectObj.class);
                        units.add("Unit I");units.add("Unit II");
                        units.add("Unit III");units.add("Unit IV");
                        Subjects s = new Subjects(subjectObj.getProfessorName(), subjectCode, subjectObj.getSubjectName(), units);
                        myCallback.onCallback(s);
                        //subjectsArrayList.add(s);
                        //Log.e("subjectArrayLIst", subjectsArrayList.toString());
                    }

                }

        }

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

        }
    });

    Log.e("holder", String.valueOf(subjectsArrayList.size()));

}
@Override
public void onBindViewHolder(@NonNull SubjectHolder holder, int position, @NonNull List payloads) {
    super.onBindViewHolder(holder, position, payloads);
}

@NonNull
@Override
public SubjectHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    //something
}

@Override
public void onBindViewHolder(@NonNull SubjectHolder holder, int position) {
//something

}


@Override
public int getItemCount()

}


Solution

  • You can't make the adapter wait for the asynchronous operation. You can either hold off on creating the adapter (and thus move the code that loads the data outside of the adapter), or make the adapter handle the fact that its data is loaded asynchronously.

    The latter you do by notifying the adapter when the data loads:

    SubjectAdapter adapter = this;
    semesterInfoReference.child("1").addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for(DataSnapshot dsa:dataSnapshot.getChildren()) {
                    //Log.e("dsa",dsa.getValue().toString());
                    String subjectCode = dsa.getKey();
                    for(DataSnapshot ds:dsa.getChildren()) {
                        SubjectObj subjectObj = ds.getValue(SubjectObj.class);
                        units.add("Unit I");units.add("Unit II");
                        units.add("Unit III");units.add("Unit IV");
                        Subjects s = new Subjects(subjectObj.getProfessorName(), subjectCode, subjectObj.getSubjectName(), units);
                        myCallback.onCallback(s);
                    }
                    adapter.notifyDataSetChanged();
                }
    
        }
    
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            throw databaseError.toException(); // never ignore errors
        }
    });