I'm using Android studio -Java
from the images below, which show my firebase, I want to fill an integer matrix which represents users in rows and posts in columns. and the values of the matrix will represent which posts the users likes and which posts the user not put like on it . ( 1 if the users like it and 0 if not).
"Likes" collection in firebase contains the 'Postid' and the children of postid are the 'userId' for each user like this post.
the problem is that when I make a nested for loop to fill the matrix, the results is wrong because firebase is Asynchronous and I want the nested loops to run synchronously to fill the matrix in the correct order . I used callbacks but I don't know a lot about dealing with it.
Is there anyone who can help me please?
the code:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recommend, container, false);
text = view.findViewById(R.id.test);
fuser = FirebaseAuth.getInstance().getCurrentUser();
getUsers(new UserListCallback() {
#Override
public void onCallback(ArrayList<String> users) {
Log.e("users", users.size()+"" );
}
});
return view;
}
private void getUsers(final UserListCallback myCallback) {
FirebaseDatabase.getInstance().getReference().child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Log.e("si", "hff" + "");
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
User user = snapshot.getValue(User.class);
users.add(user.getId());
}
myCallback.onCallback(users);
getPosts(new PostListCallback() {
#Override
public void onCallback(ArrayList<String> users) {
Log.e("posts", posts.size()+"" );
// System.out.println("Loaded "+" contacts");
}
});
Log.e("si", "fff" + "");
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
private void getPosts(final PostListCallback myCallback) {
FirebaseDatabase.getInstance().getReference().child("Posts").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Log.e("p", "pp" + "");
Post post = snapshot.getValue(Post.class);
posts.add(post.getPostid());
}
myCallback.onCallback(posts);
prepareArray(new ArrayCallback() {
#Override
public void onCallback(ArrayStructure data) {
Log.e("posts", posts.size()+"" );
// System.out.println("Loaded "+" contacts");
}
});
// prepareArray(final UserListCallback myCallback);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
private void prepareArray(final ArrayCallback myCallback) {
for(int i=0 ;i<users.size();i++){
for(int j=0;j<posts.size();j++){
int finalI = i;
int finalJ = j;
FirebaseDatabase.getInstance().getReference().child("Likes").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if(snapshot.child(posts.get(finalJ)).exists()){
Log.e("j", finalJ+": "+posts.get(finalJ) );
FirebaseDatabase.getInstance().getReference().child("Likes").child(posts.get(finalJ)).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange (#NonNull DataSnapshot d4snapshot){
if (d4snapshot.child(users.get(finalI)).exists()) {
Log.e("i", finalI+": "+users.get(finalI) );
Data.add(finalI, finalJ, 1.0);
} else {
Log.e("i", finalI+": "+users.get(finalI) );
Data.add(finalI,
finalJ,
0.0);
}
// Log.e("post", j+ "");
}
#Override
public void onCancelled (#NonNull DatabaseError error){
}
});
}
else {
Data.add(finalI,
finalJ,
0.0);
Log.e("j", finalJ+": "+posts.get(finalJ) );
}
//
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
}
myCallback.onCallback(Data);
data= Data.toArray();
for(int l=0 ;l<data.length;l++){
// Log.e("user", users.size()+"" );
for(int f=0;f<data[l].length;f++){
Log.e("post", posts.size()+"" );
Log.e("hi", data[l][f]+"" );
}
}
}
Related
I have issue with two of these listeners. Below is the scenario.
When i first load the page, in order to prevent "onChildAdded" from addChildEventListener to get fired before "onDataChange" from addListenerForSingleValueEvent, i implement following code.
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState)
{
//List object initialization
itemList1 = new ArrayList<>();
itemList2 = new ArrayList<>();
check = false;
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_nested_recycler_view, container, false);
recyclerView = view.findViewById(R.id.nestedRecyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
readData(new FirebaseCallback()
{
#Override
public void onCallBack(List<DonationRequestDetailsModel> list1, List<DonationRequestDetailsModel> list2)
{
if (list1.size() == 0)
{
confirmationAdapter1 = new ConfirmationAdapter(list2, item, location, email, getActivity());
recyclerView.setAdapter(confirmationAdapter1);
check = true;
}
else if(list1.size() == 1)
{
confirmationAdapter2 = new ConfirmationAdapter(list1, item, location, email, getActivity());
recyclerView.setAdapter(confirmationAdapter2);
check = true;
}
}
});
return view;
}
private interface FirebaseCallback
{
void onCallBack(List<DonationRequestDetailsModel> list1, List<DonationRequestDetailsModel> list2);
}
private void readData(final FirebaseCallback firebaseCallback)
{
//Loop through data in Firebase Database based on corresponding email and add data into List
reference = FirebaseDatabase.getInstance().getReference();
xreference = reference.child("Benefactor Request");
xreference.addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot)
{
for (DataSnapshot snapshot : dataSnapshot.getChildren())
{
if (snapshot.child("donaterEmail").getValue().toString().equals(email) && snapshot.child("meetingLocation").getValue().toString().equals(location) && snapshot.child("itemDescription").getValue().toString().equals(item) && snapshot.child("status").getValue().toString().equals("Accepted") )
{
itemList1.add(new DonationRequestDetailsModel(snapshot.child("benefactorName").getValue().toString(), snapshot.child("benefactorEmail").getValue().toString(), snapshot.child("dateandtime").getValue().toString(), snapshot.child("remark").getValue().toString()));
}
else if (snapshot.child("donaterEmail").getValue().toString().equals(email) && snapshot.child("meetingLocation").getValue().toString().equals(location) && snapshot.child("itemDescription").getValue().toString().equals(item) && snapshot.child("status").getValue().toString().equals("Pending") )
{
itemList2.add(new DonationRequestDetailsModel(snapshot.child("benefactorName").getValue().toString(), snapshot.child("benefactorEmail").getValue().toString(), snapshot.child("dateandtime").getValue().toString(), snapshot.child("remark").getValue().toString()));
}
}
firebaseCallback.onCallBack(itemList1, itemList2);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError)
{
}
});
xreference.addChildEventListener(new ChildEventListener()
{
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
if(check)
{
itemList2.add(new DonationRequestDetailsModel(dataSnapshot.child("benefactorName").getValue().toString(), dataSnapshot.child("benefactorEmail").getValue().toString(), dataSnapshot.child("dateandtime").getValue().toString(), dataSnapshot.child("remark").getValue().toString()));
confirmationAdapter1.notifyDataSetChanged();
}
else
{
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot)
{
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError)
{
}
});
}
}
However, when i press back from the page, and click into the page again, "onDataChange" from addListenerForSingleValueEvent gets fired before "onChildAdded" from addChildEventListener, why?
I'm trying to implement a search function on my recyclerviewer but I'm having some trouble with it. I have created the query and the method for it,but the search does not return anything at all.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
...
...
mDBListener = mDatabaseRef.addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
mUploads.clear();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Buildings upload = postSnapshot.getValue(Buildings.class);
upload.setKey(postSnapshot.getKey());
mUploads.add(upload);
}
mAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(MainActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
mButtonSearch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String searchText = mSearchView.getText().toString();
firebaseUserSearch(searchText);
}
});
and i have this outside of my onCreate
private void firebaseUserSearch(String searchText) {
Query queryRef = mDatabaseRef.child("Buildings").orderByChild("name").startAt(searchText).endAt(searchText + "\uf8ff");
queryRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChildren()) {
for (DataSnapshot postsSnapshot : dataSnapshot.getChildren()) {
final Buildings buildings = postsSnapshot.getValue(Buildings.class);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Is this the correct way to implement it?
I have the following code for deleting some data form Firebase:
public void removeGameFromWaitQueue() {
final DatabaseReference data = FirebaseDatabase.getInstance().getReference().child("games").child(mCurrentGameKey);
data.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
mCurrentGame = snapshot.getValue(DatabaseModels.Game.class);
if (mCurrentGame.getState() == DatabaseModels.Game.State.OPEN.ordinal()){
data.removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
How can i check if "data" exists, then execute all the else code? Meaning that if i create the DatabaseReference, code works. But of course if i dont create the reference, then i have null exceprion. Should i have 2 listeners for this? And if yes, how is the best way to do it?
For future reference i solved it like this:
public void removeGameFromWaitQueue() {
final DatabaseReference data = FirebaseDatabase.getInstance().getReference().child("games").child(mCurrentGameKey);
data.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
mCurrentGame = snapshot.getValue(DatabaseModels.Game.class);
if (mCurrentGame.getState() == DatabaseModels.Game.State.OPEN.ordinal()){
data.removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
public void removeAfterCheck(){
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("games")) {
removeGameFromWaitQueue();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Probably there is a better way, but this works for me in all cases as i want.
I am retrieving data from firebase database inside for loop , but problem is that firebase functions are not execute synchronously , i know firebase functions are asynchronous . is there any solution for that ?
Code :-
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModelList.clear();
for(DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
Log.d("kkkk","1st");
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModel.setRefresh_token((String)dataSnapshot.child("refresh_token").getValue());
peopleModelList.add(peopleModel);
Log.d("kkkk","2nd");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Log.d("kkkk","3rd");
}
iCallBackPeople.peopleAct(peopleModelList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Output :-
1st
3rd
2nd
But i want output like this
1st
2nd
3rd
Note :-
i searched , but never got any solution !
How to get data from Firebase Database in a loop?
Firebase addListenerForSingleValueEvent excute later in loop
I tried this but not worked for me, it is fill my list with last item !
public void listOfUsers(final ICallBackPeople iCallBackPeople) {
count=0;
//peopleModelList = new ArrayList<>();
sortedMap = new TreeMap<>();
// peopleModelList = new ArrayList<PeopleModel>(sortedMap.values());
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// peopleModelList.clear();
for(DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
//Object chatRoom = snapshot.getKey();
Log.d("kkkk","1st");
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
addItem(count,iCallBackPeople);
count++;
}
Log.d("kkkk","3rd");
}
Log.d("kkkk","end");
//iCallBackPeople.peopleAct(peopleModelList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void addItem(final int index, final ICallBackPeople iCallBackPeople) {
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModel.setRefresh_token((String)dataSnapshot.child("refresh_token").getValue());
Log.d("kkkk","2nd");
sortedMap.put(index, peopleModel);
// sortedMap will sort your list by key (in this case, key is integer)
if(sortedMap.size()==2)
{
peopleModelList = new ArrayList<PeopleModel>(sortedMap.values());
iCallBackPeople.peopleAct(peopleModelList);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I tried this but not getting all of the item only getting list with last item in snapshot
public void listOfUsers(final ICallBackPeople iCallBackPeople) {
peopleModelList = new ArrayList<>();
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
PeopleModel peopleModel;
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModelList.clear();
for(final DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
getToken(new CallBackGetToken() {
#Override
public void token(String token) {
peopleModel.setRefresh_token(token);
Toast.makeText(context, token, Toast.LENGTH_SHORT).show();
peopleModelList.add(peopleModel);
//here i am checking my peopleModelList size is equal or not to snapshot
if(peopleModelList.size()==2)
iCallBackPeople.peopleAct(peopleModelList);
}
},peopleModel);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void getToken(final CallBackGetToken callBackGetToken ,PeopleModel peopleModel) {
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
callBackGetToken.token((String)dataSnapshot.child("refresh_token").getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I think you should pass through an interface(create one and implement at this class). the method inside the interface should have a string as a parameter (it will receive your peopleModel.getChatWith() ) .
Inside your if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))you should request your interface.
I think is a solution for your asynchronously question.
Here is my solution:
int count =0;
SortedMap<Integer,PeopleModel> sortedMap;
private void set() {
count =0;
registrationReference.child(userId).child("generated_links").addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModelList.clear();
sortedMap = new TreeMap<>();
for(DataSnapshot snapshot : dataSnapshot.getChildren())
{
peopleModel = new PeopleModel();
peopleHashMap =(HashMap)snapshot.getValue();
peopleModel.setChatWith((String)peopleHashMap.get("chatWith"));
peopleModel.setNickname((String)peopleHashMap.get("nickname"));
peopleModel.setChatRoom(snapshot.getKey());
Log.d("kkkk","1st");
if(peopleModel.getChatWith()!=null && !("".equals(peopleModel.getChatWith())))
{
addItem(count);
count++;
}
Log.d("kkkk","3rd");
}
iCallBackPeople.peopleAct(peopleModelList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void addItem(final int index) {
registrationReference.child(peopleModel.getChatWith()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
peopleModel.setRefresh_token((String)dataSnapshot.child("refresh_token").getValue());
Log.d("kkkk","2nd");
sortedMap.put(index, peopleModel);
// sortedMap will sort your list by key (in this case, key is integer)
// you can get peopleModelList = new ArrayList<>(sortedMap.values);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Maybe I'm over simplifying it, but I feel like you should just use simple recursion.
List<People> peopleList;
int currentIndex = 0
private void myFirebaseMethodAction(person){
registerListener.onCallbacks(new ValueListener(){
onDataChanged(){
person.addStufforDoStuff();
if(peopleList.size > currentIndex + 1){
myFirebaseMethodAction(peopleList.get(++currentIndex)
}
}
onFailed(){
//handle it
}
}
}
Does that make sense?
Just saying walk the list with an index, simple.
btw above is pseduo code, don't copy verbatim lol.
As mentioned in docs, it's better to fetch additional data, than nest objects in database.
According to this, how can I ensure that all data came?
I came up to this solution, but I'm not sure if this will work.
mPetsReference.addValueEventListener(new ValueEventListener() {
private List<Pair<Pet, Owner>> mData;
private long mChildrenCount;
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
mData = new ArrayList<>();
mChildrenCount = dataSnapshot.getChildrenCount();
for (DataSnapshot data : dataSnapshot.getChildren()) {
Pet pet = data.getValue(Pet.class);
mOwnersReference.child(pet.getOwnerId())
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
mData.add(new Pair<>(pet, dataSnapshot.getValue(Owner.class)));
if (mData.size() == mChildrenCount) {
everythingCame();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
mChildrenCount--;
if (mData.size() == mChildrenCount) {
everythingCame();
}
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});