I'm making an app and retrieving data for profile like name, age, username, dob, etc., from a firebase database. But, when I open the app and go to the profile page, it takes about 2 to 3 seconds to refresh the text there and then display the data. I want to make that process faster. Is there anyway to do that? And is it possible to store that data within local storage of the app and display it and update that stored data later using internet and data from firebase database? The same principal I wanted to apply to images which load from firebase storage. Those take much longer than the data. Approximately 4 to 5 seconds.
This is what I'm currently using:
mDatabase = FirebaseDatabase.getInstance().getReference().child(current_user);
mDatabase.keepSynced(true);
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String first = dataSnapshot.child("firstname").getValue().toString();
String last = dataSnapshot.child("lastname").getValue().toString();
String friend = dataSnapshot.child("friends").getValue().toString();
String post = dataSnapshot.child("posts").getValue().toString();
String user = dataSnapshot.child("username").getValue().toString();
String db = dataSnapshot.child("age").getValue().toString();
String ag = dataSnapshot.child("dob").getValue().toString();
String bo = dataSnapshot.child("bio").getValue().toString();
String rela = dataSnapshot.child("relationshipstatus").getValue().toString();
firstname.setText(first);
lasttname.setText(last);
username.setText(user);
friends.setText("Friends:"+friend);
posts.setText("Posts:"+post);
dob.setText("DOB:"+db);
age.setText("Age:"+ag);
bio.setText(bo);
relation.setText(rela);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Whenever you're storing user profile in firebase first store it in local, use shared preference or room whichever suits you more, and after that whenever you do any update, first do it in local data and then sync it to firebase storage, that way the data will always be their with you until user clears it.
For this always make a check whether local data exists, if it doesn't get data from firebase storage then save it in local.
Same way use caching for storing images in local, use glide or fresco for imageloading, then add simple code for caching, so until image url changes or local data is deleted you'll always get image very fast.
Related
I'm developing a push notification app, do you have same issue:
My app generate a FCM device token and store it in a cloud database, so I can send notification messages to a device via FCM, and my database table looks like:
userid | device_token
Mary | xxxxxxxxxxxxxxxxxxxxxxxxxxxx // token1, from Mary's first device
John | yyyyyyyyyyyyyyyyyyyyyyyyyyyy
Mary | zzzzzzzzzzzzzzzzzzzzzzzzzzzz // token2, from Mary's second device
Mary | kkkkkkkkkkkkkkkkkkkkkkkkkkkk // token, from Mary's first device
.......
After Mary reinstalled this app from her first device, a new device token generated, and it is stored with token3.
How can I remove the expired device token token1, the only information I got may only be a pair of device token and an account name.
So how do you manage your device in this situation?
If "Mary" is using the same account to log in each time in your app, even if it is a new phone or reinstalled app why do you create a new token field inside the database? Why don't you always write inside the same token field so you always have access to this field. This will also send notifications only to the phone that your user is actually using right now. So each time when the user starts the app check token, if not equal write the new one inside your database. And from the server-side take those tokens and send notifications.
Am I missing something?
To do this, I would suggest using FirebaseAuth for the SignIn and SignUp process of your application. Then use generated uid as the field ID for the user inside the Realtime Database. You can get this uid with FirebaseAuth.getInstance().getCurrentUser().getUid(). So your user Mary will always have the same uid no matter what phone she uses. Always find the user inside the database with this uid and overwrite the existing Firebase token. This also means that your "users" will not be a single line field inside the database, but a more complex and better representation of a user. You can use model classes for this like:
public class User {
public long id = 0;
public long account_id = 0;
public String account_name = "";
public String first_name = "";
public String last_name = "";
public String email_address = "";
public String password = "";
public User() {
}
}
It's up to you on how to configure this. But using models is also helping you on posting and retrieving data, like this:
Creating new user:
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).setValue(user);
Retrieving data from the real-time database:
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
DatabaseReference databaseUsers = database.child("users");
User myUser = null;
Query usersQuery = databaseUsers.orderByChild("username").equalTo(uid); //you can use any value to order as you want, or you don't have to, there is many options for this query
usersQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChanged(#NonNull DataSnapshot snapshot) {
try {
for (DataSnapshot user : snapshot.getChildren()) {
myUser = user.getValue(User.class);
}
} catch (Exception e) {
e.printStackTrace();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
Inspired by this: Firebase Cloud Messaging - Managing Registration Tokens
When a token generated:
if Mary logged in:
add the device to the "Mary" Device Group.
store the device group id and connect the device group id to "Mary"'s profile in database.
if app server need to send notification to Mary, just send to the Device Group, the benifit is that you don't need to check if the device token is valid or not, Firebase Cloud Messaging discards invalid device tokens.
if no one logged in:
do nothing or just store the device token.
I want to save the data in firebase database with a descending order and I found that the solution is to add a timeStamp field with a negative value, but using ServerValue.TIMESTAMP save only the value in a positive way,so how can I save a negative timeStamp in my FireBase :
Code of model:
public class Book{
Object createdTimestamp;
String nom_livre;
String desc_livre;
String prix_livre;
String id_book;
String id_user;
public Book() {
super();
}
public Book(String nom_livre, String desc_livre, String prix_livre, String id_book,String id_user, Object createdTimestamp) {
super();
this.nom_livre = nom_livre;
this.desc_livre = desc_livre;
this.prix_livre = prix_livre;
this.id_book = id_book;
this.id_user=id_user;
this.createdTimestamp= createdTimestamp;
}
#Exclude
public long getCreatedTimestampLong(){
return (long)createdTimestamp;
}
//other getters and setters
}
Code of adding data in fireBase , im my case I'm creating a Book on addBookActivity:
private void createBook(String nom_livre, String desc_livre,String prix_livre,Object createdTimestamp) {
bookInfos=new Book(nom_livre,desc_livre,prix_livre,idLivre,id, ServerValue.TIMESTAMP );
myRefBook.child(idBook).setValue(bookInfos);
}
You have two options, and they both require a second write to the database after you initially write the regular timestamp number as a positive number.
If you only want to write data on the client app, what you can do is write your createdTimestamp as you are right now, then read that value back into the client by listening to the location that you just wrote. After you read it back in, you'll have the actual timestamp value. Then, you can easily compute the negative it and write it back where you want it (maybe revCreatedTimestamp, if you're using it to sort in reverse chronological order).
Your other option is to use Cloud Functions for Firebase to write a Realtime Database trigger to respond to writes that match the location /books/{book_id} where book_id is that string you're generating for the book. That trigger can then capture the timestamp and write back the negative version at the same location.
May I ask if there is any implementation to collect the time when the first data is updated. For example, there is a queue function in my app. When an user has taken the queue ticket, Firebase will then be updated.
Therefore, I would like to know the time that the first user in the queue.
Is there any code for this in Android Studio? Many thanks!!
The Firebase Database does not store metadata (informations like the timestamp) for CRUD operations that are performed. Because of that, you need to store this kind of data yourself by creating your own mechanism.
In fact, you need to create a new field for each child you want to trace and change the value of the timestamp every time a action is performed. The best practice within a Firebase database is to save your data as a timestamp using: ServerValue.TIMESTAMP.
Note, that when you are saving the timestamp, you are saving as a Map and when you are retrieving, you are retrieving it as a long.
To set the timestamp, I recommend you to use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Map<String, Object> map = new HashMap<>();
map.put("timestamp", ServerValue.TIMESTAMP);
rootRef.child("yourNode").updateChildren(map);
To get you data back, I recommend you using the following method:
public static String getTimeDate(long timeStamp){
try{
DateFormat dateFormat = getDateTimeInstance();
Date netDate = (new Date(timeStamp));
return dateFormat.format(netDate);
} catch(Exception e) {
return "date";
}
}
I need some advice on how to structure my database in order to query it correctly. From what I've understood the structure of the database is everything, but I am not sure I am using it correctly.
Basically my app is supposed to retrive data from an api and display that in a listview. Every item (newspost) in the listview can have comments from firebase users.
So I have "NewsPost" , "User" and "Comment" models that looks like this.'
NewsPost.class
private String id;
private String date;
private String title;
private String commentsNumber;
private List<Comment> comments;
private String imageUrl;
User.class
private String userName;
private String userAvatar;
private String firstName;
private String lastName;
private String eMail;
Comment.class
private UUID uuid;
private String postId;
private String message;
private String postDate;
private String userAvatar;
private String userName;
So this is how I tried to store it:
AND this is how I try to get the comments when a user clicks to read the comments for the specific newsPostItem in the listView with the newsPostId as a query param.
public void getComments(String postId) {
comments = new ArrayList<>();
Query query = databaseReference.child("comments").orderByChild(postId).equalTo(postId);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot issue : dataSnapshot.getChildren()) {
Comment comment = issue.getValue(Comment.class);
comments.add(comment);
}
Log.d("GETCOMMENTS", "SNAPSHOT EXISTS");
view.onCommentsLoaded(comments);
}
else{
view.onCommentsLoaded(comments);
Log.d("GETCOMMENTS", "SNAPSHOT NOT EXISTS");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
view.onCommentsLoaded(comments);
Log.d("GETCOMMENTS", databaseError.getMessage());
}
});
}
My Question is, amidoinitrite?
I got a firebase downloaded limit for this month warning (10 GB) which shouldn't happen because I am the only user right now.
Any help is appreciated!
Sidenote: The userAvatar is stored in a byte[] in the database, no good?
When using Firebase, there are a few rules that we need to keep in mind. In your particular case, i recomand you using denormalization and to change the database to be as flatten as possible. For a better understanding, i recomand you see this video, Denormalization is normal with the Firebase Database. Because Firebase Realtime Database cannot query over multiple properties, it ussaly involves duplication data. So this things are normal when it comes to Firebase. In your particular case, i recomand you doing both things.
I also recomand you read this post, Structuring your Firebase Data correctly for a Complex App. It's very well explained and will help you take the best decision.
You need to know that there is no perfect sturcture for a database. You need to structure your database in a way that allows you to read/write data very easily and in a very efficient way. If you don't know yet, Firebase has launched a new product named Cloud Firestore. Realtime Database does not scale automatically while the new Firestore does.
Currently you are adding "NewsPostID" as root node inside "comments".
This structure will consume more storage size because every new comments is added as new root inside "comments" node.
Suppose we wants to query that : Specific user's all comments for any single date. so, in this case current structure is complex to apply query.
You can change structure like :
"comments"
-"userEmail"
- "NewPostDate"
- "NewpostID:15478"
-KHZzTwazaSd (this is random id)
-"message: hey.."
-"postDate: Date + 12:10PM"
-KHZzTwazaSd (this is random id)
-"message: Nice post.."
-"postDate: Date + 12:22PM"
- "NewpostID:54478"
-KHZzTwazaSd (this is random id)
-"message: Nice day"
-"postDate: Date + 10:45PM"
You can get all user related details from user table using "userEmail" from "comments". this will decrease node size and also will be helpful to query.
I have a database in Firebase for Android and I have an object with the attributes you see in the image. An object is stored in the database with the following code:
FirebaseUser user = mAuth.getCurrentUser();
String videoId = getIntent().getStringExtra("VIDEO_ID");
minuto = player.getCurrentTimeMillis();
Watching watching = new Watching(user.getUid(), videoId, String.valueOf(minuto));
DatabaseReference mRef = database.getReference().child("Watching").push();
mRef.setValue(watching);
The problem I have is as I am using push() to store the nodes I am having duplicate data as you can see in the image.
Is there any way to avoid storing duplicate data? Knowing that I don't have my own ID to store.
Any help ?
It depends on how you define a duplicate.
A common case where people have this question is when they're storing users. There the definition of a duplicate is simple: if two user objects have the same value for uid, they're the same user. So in that case you should store the users under their uid, instead of under a push ID.
The same applies for your situation. If a single user can only watch a single video, store the nodes under Watching by the uid of the user:
Watching
"NX7...A33"
"idVideo": "b13...o4s"
"minute": "0"
But if it's the combination of uid + idVideo that is unique, store the nodes under a combined key:
Watching
"NX7...A33_b13...o4s": "0"
Now you can easily prevent duplicates, by using setValue() instead of push():
String key = user.getUid() + "_" + videoId;
ref.child("Watching").child(key).setValue(String.valueOf(minuto));
I was facing same problem like you and this is how i figure it out. I m checking email address that user entered with the one saved in my db.if any match found then show toast that email already exists otherwise save with new key.
mFirebaseDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
//If email exists then toast shows else store the data on new key
if (!data.getValue(User.class).getEmail().equals(email)) {
mFirebaseDatabase.child(mFirebaseDatabase.push().getKey()).setValue(new User(name, email));
} else {
Toast.makeText(ChatListActivity.this, "E-mail already exists.", Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onCancelled(final DatabaseError databaseError) {
}
});