I've a realtime database of only 200 B and my app has approx. 500 active users.
In the app I query the database to add data to some arraylist:
public class FirebaseApplication extends Application {
...
#Override
public void onCreate() {
super.onCreate();
...
//reference to the root of the cloud database
mDatabase = FirebaseDatabase.getInstance().getReference();
mNamesPoloSubRoot = mDatabase.child("PoloniexNames");
//listen for change in the cloud. If there is some additional item,
//add it to the list populated from hard coded items.
mNamesPoloSubRoot.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
GenericTypeIndicator<List<String>> gt = new GenericTypeIndicator<List<String>>() {};
List names = snapshot.getValue(gt);
if( names == null ) {
//Log.v(LOG_TAG_FIREBASE,"No extra coins");
}
else {
// Log.v(LOG_TAG_FIREBASE,"extra coins!" );
additionalNamesPolo = new ArrayList<String>(names);
//attach the new coins at the bottom of the hard coded list
NamesPoloniexA.addAll(additionalNamesPolo);
}
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.e(LOG_TAG_FIREBASE, "Failed to read value.", error.toException());
}
});
...
}
}
I've 9 of such listeners. The data consumptions is approx. 100 MB every day!
At this rate I will soon hit the 10GB/month limit. If I'm correct this data should be loaded every time a user open the app and every time I change remotely the data from server (this happens very rarely, like 2 times in a month). If opening the app loads, let's say, 1 KB, then this means that app should have been opened more than 100.000 times a day, and this it's not true by at least an order of magnitude.
My database is something like:
{
"PoloniexAbbreviations" : [ "LGD", "PTOY", "PTOY" ],
"PoloniexCurrencyPairs" : [ "ETH_LGD", "BTC_PTOY", "ETH_PTOY" ],
"PoloniexNames" : [ "Legends", "Patientory", "Patientory" ]
}
Notice that many of the children called by the app are very often null (since there are no data to add remotely to the app), so the entire database can even be empty, but some data seems to be downloaded anyway (the size of the database, even if null, is not zero).
What I'm doing wrong? Or it's an issue of Firebase?
Related
Issue:
Im sure im missing something super easy, but I am not able to figure it out.
I have an if statement for a recycler view which basically says to pull in all data of the current user and put it in a recycler view.
What I want to happen:
If there is no data for the current user (so if the recycler view is empty), then display a particular image.
What's currently happening:
The image continues to display, even when the current user's data (recycler view) is displayed.
The image is displaying over the recycler view.
I need the image to be "GONE", if the recycler view is displayed with the current user data.
What I Believe The Issue Is:
I believe this is happening because the system is thinking that because there are other users that are not the current users that exist in the database, then display the image. However, im trying to figure out how to only display the image, if there is no data for the current user. Sorry if I didn't explain this correctly. Let me know if you have any questions. Thank you so much for the help!
Additional Notes
Let me know if you need to see the xml but I have already checked and the image is set to gone on the xml.
private void fetchInviteList() {
String currentUser;
currentUser = mAuth.getUid();
DatabaseReference notificationInviteRef = FirebaseDatabase.getInstance().getReference().child(Strings.InvitesReference);
notificationInviteRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot1) {
ArrayList<Model_Notifications_Invite> model_notifications_invites = new ArrayList<>();
for (DataSnapshot snapshot2:snapshot1.getChildren()) {
for (DataSnapshot snapshot3:snapshot2.getChildren()) {
String notificationPath = snapshot3.child("inviteeUserId").getValue(String.class);
if(notificationPath.equals(currentUser)){
Model_Notifications_Invite invites = snapshot3.getValue(Model_Notifications_Invite.class);
model_notifications_invites.add(invites);
} else if (!notificationPath.equals(currentUser)){
happyImage.setVisibility(View.VISIBLE);
}
}
}
myNotificationsAdapter.updateNotificationsList(model_notifications_invites);
}
#Override
public void onCancelled(DatabaseError databaseError) {
//throw databaseError.toException();
}
});
}
JSON
The first node is like the group id.
The child directly underneath that, is the current user id.
There is another child with the same user id thats called - "inviteeUserId."
"Invites": {
"-NAHHVbARBzsi0mCk4j9": {
"DrpEs5nJH4RVVdOXlgVB1EJQQDz1": {
"inviteDate": "August 25",
"inviteTime": "02:39:33 PM",
"inviteeUserId": "DrpEs5nJH4RVVdOXlgVB1EJQQDz1",
"inviterUserId": "iJ8ZuvIPtEd4pdKiychcnskgyHU2",
"bookId": "-NAHHVbARBzsi0mCk4j9"
},
"oWaE8TWfyAalpWyD9oUdV80ocsw2": {
"inviteDate": "August 25",
"inviteTime": "04:08:28 PM",
"inviteeUserId": "oWaE8TWfyAalpWyD9oUdV80ocsw2",
"inviterUserId": "iJ8ZuvIPtEd4pdKiychcnskgyHU2",
"bookId": "-NAHHVbARBzsi0mCk4j9"
}
}
In an android app I have made, the home page shows a list a games in a listview, each row being custom made to include a textview and an imageview and some buttons. The imageview's src comes from downloading th image from my google cloud firebase, and the functionality of this works well, but when the listview is scrolled through there is an issue. The images seem to be unloaded when scrolled away from, which causes a bit of lag when they are reloaded once scrolled back to. I imagine this is built in to prevent a listview from loading many high resolution images and keeping them loaded, but for my list, keeping the images loaded won't be a problem. Is there a way I can turn this off and just keep the images loaded? Here is a video of the problem:
https://www.youtube.com/watch?v=FYYJWZcvBy4&feature=youtu.be
here is the code of the listview and getting the image from firebase:
public void showData(DataSnapshot dataSnapshot){
TextView toolbarTitle = findViewById(R.id.toolbarTitle);
toolbarTitle.setText("Popular Games");
ArrayList<GameInformation> PopularGames = new ArrayList<>();
Iterator<DataSnapshot> items = dataSnapshot.child("Games").getChildren().iterator();
PopularGames.clear();
while(items.hasNext()){
GameInformation game = new GameInformation();
DataSnapshot item = items.next();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
Iterator<DataSnapshot> itemsDeep1 = item.child("consoles").getChildren().iterator();
while(itemsDeep1.hasNext()){
DataSnapshot itemDeep = itemsDeep1.next();
game.setConsoles(itemDeep.getValue(String.class));
}
Iterator<DataSnapshot> itemsDeep2 = item.child("genres").getChildren().iterator();
while(itemsDeep2.hasNext()){
DataSnapshot itemDeep = itemsDeep2.next();
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());
ArrayAdapter adapter = new CustomListAdapter(this, PopularGames);
mListView.setAdapter(adapter);
}
getting the image:
storageRef.child("Games/"+singleGame.getPicturePath()+".jpg").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Glide.with(getContext()).load(uri).into(gameImageID);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});
The delay you're facing is because there are 2 operations (each one takes a few secs to complete) being performed when you get each image:
The image is read from Firebase Storage using getDownloadUrl()
The image is displayed with Glide
I recommend that when you store the images, save their Download URL to the database instead of saving their path. This way, you'd no longer need to call for getDownloadURL(). You'd simply load the image with a single operation:
Glide.with(getContext()).load(singleGame.getPicturePath()).into(gameImageID);
Not related to the question:
You can simplify your code by using a forEach loop (instead of while) in your Iterators:
PopularGames.clear();
for(DataSnapshot item : dataSnapshot.child("Games").getChildren()){
GameInformation game = new GameInformation();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
for(DataSnapshot itemDeep: item.child("consoles").getChildren()){
game.setConsoles(itemDeep.getValue(String.class));
}
for(DataSnapshot itemDeep: item.child("genres").getChildren()){
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());
I have a service scheduling screen.
But there is a problem:
I need to check if the date and time the user is trying to schedule is available or reserved.
Structure DB:
Companies
-Company ID (Document)
--name
--phone
---Schedules (Collection)
------Event1
--------Hour: 08:30
--------Date: 01/01/2018
------Event2
--------Hour: 09:00
--------Date: 05/01/2018
------Event3
--------Hour: 10:30
--------Date: 01/002/2018
I access Scheduling data with this code:
String dateExample = "01/01/2018"
String hourExample = "08:30"
FirebaseFirestore mDB = FirebaseFirestore.getInstance();
CollectionReference mDBCompaniesSchedules = (CollectionReference) mDB.collection("Companies").document(mId_Company).collection("Schedules")
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
for (Schedules document : documentSnapshots.toObjects(Schedules.class)) {
String dtSchedules = document.getSchedules_date();
String hourSchedules = document.getSchedules_hour();
if ( dtSchedules.equals(dateExample) && hourSchedules.equals(hourExample) ){
//Execute a "Toast" and closes the operation
} else {
//Call up the scheduling function.
startScheduling();
}
}
}
})
Process:
I need to run this code and go through all the documents in that collection. I need to check and analyze whether the date and time of the schedule already exists.
If there is: Show a Toast and block.
If there is no: Executing a specific function for the schedule record ("startScheduling()").
Problem:
When the data exists (it will only be once) then it will work.
When there is no data, it falls into the ELSE loop. And it is executing several times the same function "startScheduling();".
I need some way to go through this collection and when I do not find any results, the function "startScheduling (), be executed only once.
This how a for loop works. It will continue iterate till the last element to see if the condition is true or not. With other words, your if-else statement is triggered for every iteration in the loop. It means that, if the condition is true it will go with the if part, if the condition is false it will go with else part, for each and every element.
There are two ways in which you can solve this. One would be to break the loop once the condition was fulfilled. But this means that will iterate till it gets that element. Second, would be to change the logic of your code. Use first the if statement and second iterate.
Edit: The best option in this case would be to query your database using whereEqualTo() method.
Query query = db
.collection("Companies")
.document(mId_Company)
.collection("Schedules")
.whereEqualTo("dtSchedules", dateExample)
.whereEqualTo("hourSchedules", hourExample);
In which dateExample and hourExample are the actual values with which you want to compare.
To count the number of documents in a Collection, please use the following code:
public void onSuccess(QuerySnapshot documentSnapshots) {
if(documentSnapshots.size() == 0) {
startScheduling();
}
for (Schedules document : documentSnapshots.toObjects(Schedules.class)) {
String dtSchedules = document.getSchedules_date();
String hourSchedules = document.getSchedules_hour();
}
}
I create an app with Firebase. There is an issue that i can't solve, and didn't find it talked here.
In this method I want to check if some data is already in the server. If not - I want to add it (the code of adding works well. The Firebase database is being changed as I want). so I'm using onDataChange method as following:
public boolean insertNewList(String title)
{
System.out.println("start: ");
final boolean[] result = new boolean[1];
result[0]=false;
final String group = title;
mRootRef = some reference...
mRootRef.addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
System.out.println(0);
if (dataSnapshot.child(group).getValue() != null)
{
System.out.println(group + " is already exist");
System.out.println(1);
}
//write the data.
else
{
mRootRef=mRootRef.child(group);
mRootRef.push().setValue("some data");
System.out.println(2);
result[0]=true;
}
}
#Override
public void onCancelled(DatabaseError databaseError)
{
}
});
System.out.println(3);
return result[0];
}
But what realy happens is this output:
begin:
3 (just skip on onDataChange method and return false).
some print after calling the function
0 (goes back to function and enter to onDataChange method)
2 (finally goes where I want it to go)
0 (for some reason enters twice :( )
1
And because of that i receive wrong results in this function.
Can you help please?
Replace
mRootRef.addValueEventListener(new ValueEventListener()
with
mRootRef.addListenerForSingleValueEvent(new ValueEventListener()
When you add the the value to firebase, "addValueEventListener" called again, not like addListenerForSingleValueEvent that shots only once anywhy.
The output that you showed us looks normal to me. Let me try to explain:
begin: // a) this comes from the System.out.println("begin")
3 // b) you DECLARE your value event listener and then you run
// the System.out.print("3") statement
0 // c) you just added a listener so firebase calls you, and
// your listener fires
2 // d) this is the first time your listener fires, so you go
// into the block called "write the data"
0 // e) since you just wrote the data, firebase calls you again
1 // f) this time, since the data has been written, you go into
// the block called "is alraedy exist"
This is normal behaviour for firebase.
In c), firebase always calls you back one time when you declare a listener.
In e), firebase calls you because the data changed.
But in b), you are only declaring your listener, not yet running it, so the statements after this declaration are executed and you see "3" before anything else happens.
Background:
I am working on a group messaging app for Android using parse.com as my backend.
Working:
I Have a ParseQueryAdapter which queries for ChatGroups from the local datastore using:
public ChatGroupAdapter(final Context context) {
super(context, new ParseQueryAdapter.QueryFactory<ChatGroup>() {
public ParseQuery<ChatGroup> create() {
ParseQuery<ChatGroup> chatGroupQuery = ParseQuery.getQuery(ChatGroup.class);
chatGroupQuery.fromLocalDatastore();
Log.d(ChatGroup.class.getName(), "Constructor query: " +
chatGroupQuery + " made");
//noinspection unchecked
return chatGroupQuery;
}
});
}
The fragment that contains it listens to it by adding a ParseQueryAdapter.OnQueryLoadListener. If the ParseQueryAdapter cannot retrieve the ChatGroups from the local datastore, it retrieves it from parse, pins it and then triggeres the ParseQueryAdapter to retry loading from the local datastore using: .loadObjects();. Here is a code snippet from my fragment:
#Override
public void onLoaded(List<ChatGroup> groups, Exception e) {
l.d("onLoaded called");
if (e != null) {
e.printStackTrace();
updateLocalDatastore();
} else if (groups == null) {
l.w("Received null group list.");
updateLocalDatastore();
} else if (groups.size() == 0) {
l.w("Received group list of size zero.");
updateLocalDatastore();
} else {
l.d("Chat groups loaded successfully");
}
}
private void updateLocalDatastore() {
final ParseQuery<ChatGroup> groupQuery = ParseQuery.getQuery(ChatGroup.class);
groupQuery.findInBackground(new FindCallback<ChatGroup>() {
#Override
public void done(final List<ChatGroup> chatGroupList, ParseException e) {
if (e != null) {
e.printStackTrace();
} else {
if (chatGroupList != null) {
ParseObject.pinAllInBackground(chatGroupList, new SaveCallback() {
#Override
public void done(ParseException e) {
if (e != null) {
e.printStackTrace();
} else {
l.d("Successfully pinned: " + chatGroupList.size() +
" groups to local datastore. Reloading adapter");
groupAdapter.loadObjects();
}
}
});
} else {
l.e("Received null group list from cloud! " +
"Local datastore update failed!");
}
}
}
});
}
Result:
And here is a simplified log of the execution:
ChatFragment: onCreate
ChatFragment﹕ Initializing chat groups
ChatFragment﹕ Loading chat groups
ChatFragment: onStart
ChatFragment: onResume
ChatFragment﹕ onLoaded called
ChatFragment﹕ Received group list of size zero.
ChatFragment﹕ Successfully pinned: 3 groups to local datastore. Reloading adapter
ChatFragment﹕ Loading chat groups
ChatFragment﹕ onLoaded called
ChatFragment﹕ Received group list of size zero.
ChatFragment﹕ Successfully pinned: 3 groups to local datastore. Reloading adapter
ChatFragment﹕ Loading chat groups
ChatFragment﹕ onLoaded called
ChatFragment﹕ Received group list of size zero.
ChatFragment﹕ Successfully pinned: 3 groups to local datastore. Reloading adapter
ChatFragment﹕ Loading chat groups
ChatFragment﹕ onLoaded called
ChatFragment﹕ Received group list of size zero.
It seems obvious to me that the ParseQueryAdapter should retrieve a list of 0 the first time which triggers the updateLocalDatastore() which pulls 3 ChatGroups from parse, pin them and then it should receive 3 from the local datastore. However as seen in the log, it continues to find 0 ChatGroups from the local datastore even after pinning them successfully!
The project setup seems fine:
Parse.enableLocalDatastore(this);
Parse.initialize(this, AppProps.properties.appId, AppProps.properties.clientKey);
ParseUser.enableRevocableSessionInBackground();
Parse.setLogLevel(Parse.LOG_LEVEL_DEBUG);
ParseObject.registerSubclass(ChatGroup.class);
What am I doing wrong?! Am I missing something?
I implemented the exact same logic with ParseUser and it worked flawlessly! There seems to be something wrong with doing the same with GroupChat:
#ParseClassName("ChatGroup")
public class ChatGroup extends ParseObject {
Some say that it is a parse bug with relations in the local datastore. But I am not even using relations. Even a simple query from the local datastore doesn't work.
Although I couldn't fix the problem, I discovered what the issue was. The issue is related to an underlying bug with the Parse SDK of Android: ParseQuery gives 0 objects when querying from local datastore even after successfully pinning