Firebase search is so slow - android

I have an android app which contains a search with firebase option and search with the api I'm using, the api takes a matter of seconds to print the data on the screen, while firebase takes minutes to print data and is slow to query the database
Here's my code:
public void firebaseSearch(String name) {
mFirebaseSearchList.clear();
mSearchLoading.setVisibility(View.VISIBLE);
mFirebaseSearchAdapter.setData(new ArrayList<_Release>());
mDatabase.child("releases")
.child(mRegion)
.child("data").orderByChild("game/search_name").startAt(name.toLowerCase())
.endAt(name.toLowerCase()+"\uf8ff")
.limitToFirst(10)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
mSearchLoading.setVisibility(View.GONE);
if (dataSnapshot.hasChildren()) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
_Release release = data.getValue(_Release.class);
if (release != null) {
mFirebaseSearchList.add(release);
mFirebaseSearchAdapter.notifyDataSetChanged();
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
mSearchLoading.setVisibility(View.GONE);
}
});
}
Listener is set to onTextChange
mGameSearchView.setOnQueryTextListener(new MaterialSearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
mGameSearchView.showSearch();
mGameSearchView.requestFocus();
if (currentSearchId == 2) {
apiSearch(query);
}
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
if (currentSearchId == 1) {
firebaseSearch(newText);
}
return true;
}
});

Related

How to execute firebase function synchronously inside for loop?

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.

RxJava and Firebase database

I'm trying to refactor my synchronization procedure with rxJava support.
But I've faced a strange (for me) error.
Initially, I execute 'sync' procedure. Then in 'onCompleted' I execute syncPart2. It's the same procedure (but with others nodes to sync)
In the 'syncPart2' I get 'error=DatabaseError: Permission denied'. With database rules everything ok, this error appears on the different nodes (and current sync works fine).
Basically, I have 16 nodes to sync one by one, exactly in specific order. Maybe I've chosen wrong Rx operation to do that? By the way, if I use only one 'concat' everything ok! But I have more than 9 (max size of 'concat' args) nodes to sync.
public class RxFirebaseDatabase {
#NonNull
public static Observable<DataSnapshot> observeSingleValueEvent(#NonNull final Query query) {
return Observable.create(new Observable.OnSubscribe<DataSnapshot>() {
#Override
public void call(final Subscriber<? super DataSnapshot> subscriber) {
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(dataSnapshot);
subscriber.onCompleted();
}
}
#Override
public void onCancelled(DatabaseError error) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(new RxFirebaseDataException(error));
}
}
});
}
});
}
}
public static void sync() {
Observable.concat(
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DELETED_OBJECTS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.MSI_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.COURSES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.ALLERGIES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.PHONES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.MEDICINES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.PROFILES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.ANALYSES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DIAGNOSES_NODE))
)
.observeOn(Schedulers.io())
.subscribe(new Subscriber<DataSnapshot>() {
#Override
public void onCompleted() {
syncPart2();
}
#Override
public void onError(Throwable e) {
Log.d(AppConstants.TAG_SYNC, "The error appears: " + e.getMessage());
}
#Override
public void onNext(DataSnapshot dataSnapshot) {
GenericClass genericClass = retrieveInfoAboutNode(dataSnapshot);
if (genericClass.getMyType() == DeletedObject.class) {
handleDeletedObjects(dataSnapshot);
} else if (genericClass.getMyType() == MedicineSchedulerItem.class) {
handleMSI(dataSnapshot);
} else if (genericClass.getMyType() == MedicineCourse.class) {
handleMedicineCourse(dataSnapshot);
} else {
handle(dataSnapshot, genericClass);
}
}
});
}
public static void syncPart2() {
Observable.concat(
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.HOSPITALS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.RECOMMENDATIONS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.USER_FILES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.SPECIALIZATIONS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DOCTORS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DOCTOR_VISITS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.PHOTOS_NODE))
)
.subscribe(new Subscriber<DataSnapshot>() {
#Override
public void onCompleted() {
EventBus.getDefault().post(new FirebaseEvents().new SyncFinished().new AllTasksFinished());
}
#Override
public void onError(Throwable e) {
Log.d(AppConstants.TAG_SYNC, "The error appears: " + e.getMessage());
}
#Override
public void onNext(DataSnapshot dataSnapshot) {
GenericClass genericClass = retrieveInfoAboutNode(dataSnapshot);
handle(dataSnapshot, genericClass);
}
});
}
Actually, permissions were not the cause of this issue.
I've had 'InvocationTargetException' because of Realm incorrect thread. But why the error was 'permissions denied' is still puzzle for me.

Android - Return boolean value relative Firebase

public boolean checkGold(final int gold){
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
goldparse = Integer.parseInt(value);
if (gold > goldparse){
/*Return*/
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
return false;
}
I have method to check gold in outside but how to return false in method onDataChange. Thanks.
Firebase Database is asyncronous, and its flow is outside the regular flow that we usually do. In this post, I tried to explain that (just for information)
And in this case, you do something like this, right?
boolean iWantValue = checkGold(10);
if (iWantValue) {
// do someting
} else {
// do something else
}
...
public boolean checkGold(final int gold) {
... // content here are still the same as mentioned in question
}
But if we know it is asyncronous, it should be like this:
int currentGold = 0; // place gold here so it can be accessed anywhere
...
// wherever you want to check gold, type this
currentGold = 10; // update this value first, right?
checkGold();
...
public void checkGold() {
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
goldparse = Integer.parseInt(value);
if (currentGold > goldparse){
// do something
} else {
// do something here
}
}
...
});
}
Or if you are like me who want something tidy:
public void checkGold() {
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
doSomethingOrNot(Integer.parseInt(dataSnapshot.getValue(String.class)));
}
...
});
}
private void doSomethingOrNot(int goldparse) {
if (currentGold > goldparse){
// do something
} else {
// do something here
}
}
It needs time to be familiar with this, but it's worth it. Hope this helps

Android Firebase custom RecyclerView item click issue

I have to use custom RecyclerView because I don't want to update to list real time.
How do I get an id if I want to go into the details of the data? As in FirebaseRecyclerAdapter.
final String uid = getRef(position).getKey();
I added postId, my posts table, and I wrote the following code. But when click on the image, it goes to the last added image to the list. And when I click upVote, every item goes crazy and they click upVote too.
First, am I on the right track to update the list only when I want to? Second, why is everything going crazy?
PostAdapter
public PostRecyclerAdapter(Context context, Query query) {
this.context = context;
this.query = query;
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
posts.clear();
for (DataSnapshot data : dataSnapshot.getChildren()) {
posts.add(data.getValue(Post.class));
}
Collections.sort(posts, new Comparator<Post>() {
#Override
public int compare(Post o1, Post o2) {
Long a = o1.getCreatedDate();
Long b = o2.getCreatedDate();
if (a < b) {
return -1;
} else if (a == b) {
return 0;
} else {
return 1;
}
}
});
notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onBindViewHolder(final PostViewHolder viewHolder, int position) {
model = posts.get(position);
postId = model.getPostId();
viewHolder.setTitle(model.getTitle());
viewHolder.setImage(context, model.getImage());
viewHolder.setUpVote(postId);
viewHolder.imvImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, SinglePostActivity.class);
intent.putExtra(Enums.PostKeys.postId.getValue(), postId);
context.startActivity(intent);
}
});
viewHolder.imbUpVote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!checkAuthUser()) {
context.startActivity(new Intent(context, SignUpActivity.class));
return;
}
processVote = true;
Singleton.getDbPostDownVote(postId).child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (processVote == true) {
if (dataSnapshot.hasChild(getUserId())) {
Singleton.getDbPostDownVote(postId).child(postId).child(getUserId()).removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Singleton.getDbPostUpVote(postId).child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (processVote == true) {
if (dataSnapshot.hasChild(getUserId())) {
Singleton.getDbPostUpVote(postId).child(postId).child(getUserId()).removeValue();
processVote = false;
} else {
Singleton.getDbPostUpVote(postId).child(postId).child(getUserId()).setValue(0);
processVote = false;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
}
PostViewHolder:setUpVote
public void setUpVote(final String postId) {
Singleton.getDbPostUpVote(postId).child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild(getUid())) {
imbUpVote.setImageResource(R.drawable.vote_up_active);
} else {
imbUpVote.setImageResource(R.drawable.vote_up_passive);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
How do I get an id if I want to go into the details of the data?
Usually the id is a node in your db. As you can see in
final String uid = getRef(position).getKey();
getKey returns tha value of the node in db.
In your case to avoid sorting the list with comparator i would just structure the data like so:
20170111
title : some title
text : some text
20170112
title : some title
text : some text
This way data is going to be sorted by the nodes, which is the date, by Firebase. If you want to be more precise you can also add hours and minutes.
First, am I on the right track to update the list only when I want to?
No.
Calling addValueEventListener() is going to trigger the code inside the listener each time the value in your db changes. In other words, its realtime.
Use addListenerForSingleValueEvent() insted. It fires only once.
Second, why is everything going crazy?
Very important thing about onDataChange() is that it fires not only when the value changes but also the first time you set the listener. That is why everything is getting voted up when you click one item.

How to detect if ValueEventListener has fetched data or not in Firebase

I'm using following code to fetch the data from Firebase DB but as it makes network request in background thread so I want to wait till it completes the request and get a value. For example,
boolean isAvailable=false;
usernameReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
isAvailable = true;
}
#Override
public void onCancelled(DatabaseError databaseError) {
progressBar.setVisibility(View.GONE);
}
});
if(isAvailable){
//do something here
}
else{
//do something here
}
This snippet always execute the else part so I want to wait till the variable isAvailable get the value from database then further Execution will take place.
First create an interface like this:
public interface IsAvailableCallback {
void onAvailableCallback(boolean isAvailable);
}
Suppose your above code is in this method which takes interface object to trigger callback like :
public void isAvailable(IsAvailableCallback callback) {
boolean isAvailable=false;
usernameReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
isAvailable = true;
//this will trigger true
callback.onAvailableCallback(isAvailable);
}
#Override
public void onCancelled(DatabaseError databaseError) {
progressBar.setVisibility(View.GONE);
//this will trigger false
callback.onAvailableCallback(isAvailable);
}
});
}
Call this method like :
isAvailable(new IsAvailableCallback() {
#Override
public void onAvailableCallback(boolean isAvailable) {
//you will get callback here, Do your if condition here
}
}

Categories

Resources