I'm using both Google Firebase and Room Database to store Event objects I have created, containing all the details of a given event. I use Firebase for online storage and user interaction, and I use Room Database for an easier, persistent RecyclerView implementation.
My problem is that my function for populating the Room Database with events in the user's radius doesn't seem to execute at all in NEITHER an asynchronous task NOR a Room Database callback that overrides the onCreate method. The function was fully debugged while it was used on the main thread, so I think the reason why it isn't working right now has to do with my lack of understanding of how asynchronous tasks work.
Here is my function, currently within the onCreate method of the Room Database class:
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference().child("events").child("eventid");
if (ref != null) ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Event event = dataSnapshot.getValue(Event.class);
if (event != null) {
LatLng user_location = new LatLng(current_user.get(0).getLatitude(),
current_user.get(0).getLongitude());
LatLng event_location = new LatLng(event.getLatitude(), event.getLongitude());
int distance_preference = current_user.get(0).getDistance();
double distance_between_user_and_event = SphericalUtil
.computeDistanceBetween(user_location, event_location) / 1609.344;
if (distance_between_user_and_event <= distance_preference) {
eventDao.insert(event);
}
}
}
#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) {
}
});
In an asynchronous task, I can't really debug it since it isn't on the main thread (unless I don't know how lol), and this code doesn't seem to execute at all when in the onCreate method for Room Database. There's obviously something I'm missing here.
Also, it's worth saying that my EventDao, EventDatabase, and EventRepository have all been fully debugged. I can use all of them perfectly fine at runtime -- it's just this early population task that isn't working!
Thanks so much for any help!!
Similar project created by me which uses
Firebase authentication to login user
Save and cache user notes to
sql lite database with Room
Save user notes to firebase base
database
✍️ Simple Note Making App use Sqllite Room 🧰 for caching the notes and 📥 Firebase Database for online storage
https://github.com/LanguageXX/Simple-Note-App-with-Online-Storage
Hope that helps you
Related
i am trying to get reference or snapshot of newly added child on firebase
i have solve it by different way
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
}
myRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
}
My question is that both are return a complete snapshot means all this nodes in firebase is there any way getting child which newly added ("this will improve speed")
When you attach a listener to a location or a query, the listener will initially fire with all data that exists at that location or that matches the query. The database has no concept of "new" data, although your application may have that concept.
There are a few options to reduce the data that is retrieved, or the data that is passed to your listener:
If you're using disk persistence, the application may have a previous snapshot of the data in its disk cache. In that case, only the delta between the previous snapshot and the current snapshot is retrieved from the server. But the entire current snapshot is passed to your listener, so that you can render the UI correctly for the while current data.
If you only want to process the data since a certain moment, you'll need to only request that data from the database. You can typically do this by storing the key of the data you last processed in your local state, and then using a query like this to only get the new update:
myRef.orderByKey().startAt("mostRecentKeyWeHaveAlreadyProcessed").add...Listener
So I have firebase initialised in my acitivity.
mFirebase = FirebaseDatabase.getInstance();
mDatabaseReference = mFirebase.getReference("buses");
mBusReference = mDatabaseReference.child(mSelectedBusModel.getRegistrationNo());
mValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
...
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.e("Data", "Cancelled");
databaseError.toException().printStackTrace();
}
}
mBusReference.addValueEventListener(mValueEventListener);
I have a singleton that extends Application and inside the onCreate method I have enabled firebase persistence:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Now my problem is when I go offline and reopen my activity the data has not been cached, nothing loads. I have read the docs and done everything mentioned but it still does not work.
What I want to do is enable my app to cache data already loaded from firebase so it's always available even after the app has been closed and re-opened.
Please note no errors are displayed in my console.
Just using setPersistenceEnabled(true) doesn't cut it. You must change your listener because you save the data in your disk but you dont handle them. You must use something like this (it was taken from firebase documentation i have used it for my app and worked for me).
--- edit ---
If you want to keep your data synced you must use ref.keepSynced(true);. The below piece of code is what i used to retrieve cached data from reference. When i am offline the child returns the values normally.
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("user");
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot snapshot, String previousChild) {
System.out.println(snapshot.getValue());
}
});
here is my function to get data :
public void retrievedata(){
FirstRef.child(obj.getsEventID()).orderByChild("date").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
if (dataSnapshot.exists())
{
DisplayMessages(dataSnapshot);
}
}
#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) {
}
});
}
private void DisplayMessages(DataSnapshot dataSnapshot) {
Iterator iterator = dataSnapshot.getChildren().iterator();
String Article = (String) ((DataSnapshot) iterator.next()).getValue();
String Key = (String) ((DataSnapshot) iterator.next()).getValue();
String Organisateur = (String) dataSnapshot.child("name").getValue().toString();
String date = (String) dataSnapshot.child("date").getValue().toString();
Date resultdate = new Date(Long.parseLong(date));
String date2 = DateFormat.format(resultdate).toString();
ListOfArticles.add(0,new ListItemTypeOne(Key, Article, Organisateur, date2));
adapter.notifyDataSetChanged();
}
Let's suppose I have 10 articles, they are kept in the disk memory thanks to :
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Now, while I was offline someone added 2 more articles which makes 12.
If I go online, and execute the function "retrieve data", will it simply call the onchildadded with the 10 child in the memory and the 2 new child from the firebase database or will it download all of the 12 childs from firebase ?
When you attach a listener for data that is already present on the device, Firebase immediately satisfies the listener with the data that it has on the device.
So the Firebase client will immediately call your onChildAdded with the 10 child nodes that we persisted earlier.
It then sends a request to the server to get the most up to date version. It does this with a so-called delta-sync, meaning that it by sending a hash value of the local state, which the server compares to the current state in the database. The server then sends the delta back, which the Firebase client then uses to update its internal snapshot and the state on disk.
If there are any changes, the Firebase client then fires the correct local events to allow your application to update to the new state. So in the case where two child nodes were added, it will call onChildAdded for each of those.
If you were to use a listener with a limit, say limitToLast(10), then the Firebase client would also call onChildRemoved for the two children that are no longer within that query (since they were pushed out by the new children).
The previously cached 10 children will be loaded from disk and not transferred from the server again.
I've got a Firebase Database with User data. And I have a User class with this method:
private void getFromFirebase(){
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference reference = database.getReference( FirebaseReferences.USERS ).child( userId );
reference.addValueEventListener( new ValueEventListener(){
#Override
public void onDataChange( #NonNull DataSnapshot dataSnapshot ){
display_name = dataSnapshot.child( "username" ).getValue( String.class );
photo = dataSnapshot.child( "profile_image" ).getValue( String.class );
}
#Override
public void onCancelled( #NonNull DatabaseError databaseError ){
}
} );
}
In the MainActivity.java, I want to get the profile photo from the database and show it in an ImageView. Problem is that Firebase is asynchronous and it returns immediately, so I can't just call myUser.getFromFirebase() because myUser will still have all null values. I searched a lot but can't find the solution, since I don't want my User class to interact at all with the UI (I want to use a 3-tier methodology).
I tried creating an AsyncTask extension class, but it has the same problem, because the issue is at the User class. I also tried the CountdownLatch approach, but since the value is already on the database, the onDataChange method never gets called at all!!
Does anyone has any idea how to solve this? I'm sure it's extremely easy, because it's not a weird scenario, but I'm so stucked...
Create callback listener, like below
public interface OnDataReceiveCallback {
void onDataReceived(String display_name, String photo);
}
Modify method to pass callback
private void getFromFirebase(OnDataReceiveCallback callback){
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference reference = database.getReference( FirebaseReferences.USERS ).child( userId );
reference.addValueEventListener( new ValueEventListener(){
#Override
public void onDataChange( #NonNull DataSnapshot dataSnapshot ){
display_name = dataSnapshot.child( "username" ).getValue( String.class );
photo = dataSnapshot.child( "profile_image" ).getValue( String.class );
callback.onDataReceived(display_name,photo);
}
#Override
public void onCancelled( #NonNull DatabaseError databaseError ){
}
});
}
Final call of getFromFirebase
getFromFirebase(new OnDataReceiveCallback(){
public void onDataReceived(String display_name, String photo){
// do something
}
});
You will have to arrange for your views to be updated with each callback you receive to onDataChange. If you don't want your callback to directly modify views, you will need to adopt some form of app architecture to abstract your repository (Realtime Database) from your views.
This is not "extremely easy". Also, you have a lot of choices for app architecture (MVP, MVC, MVVM), and various frameworks to help with this (such as Android's own LiveData). What you are venturing into is highly opinionated, and involves writing a lot more code than you have here.
I can point you to a repository that uses Jetpack's Android Architecture Components as app architecture for a demo app that uses both RTDB and Firestore, but you'll see that it's a lot of lines of code, and it's also just my opinion about how to get things done. You will find lots of other opinions out there.
is there any ways to read data from Firebase once the Activity is loaded. At this moment I am using the regular valueEventListener, but in order for it to work, there has to be some sort of a change in the database
mDatabaseReference.child("Users").child(mUser.getUid()).
child("Posts").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
arrayOfQuestionForms.clear();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
QuestionForm tempQuestionForm = postSnapshot.getValue(QuestionForm.class);
arrayOfQuestionForms.add(tempQuestionForm);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
What I am looking for is some way to read data from Firebase without using listeners. I have looked at other similar posts but I don't think there is a clear answer for this yet.
There is no way for reading data from a Firebase database without using listeners. Everything is about listeners when it comes to Firebase. It's true that when setting a value, we just need to use a method named setValue() directly on the reference. Unfortunately, there is no method within Firebase, let' say getValue(), which acts in the same way as setValue().
To solve this, i recommend you using addListenerForSingleValueEvent.
Add a listener for a single change in the data at this location. This listener will be triggered once with the value of the data at the location.
in order for it to work, there has to be some sort of a change in the database
This is not true and a common source of confusion for developers.
With your current code, Firebase will immediately start reading the data from the server. Once it gets that data, it invokes your onDataChange().
From the documentation:
This method is triggered once when the listener is attached and again every time the data, including children, changes.
for such purpose I used different kind of listener - ChildEventListener. It has different #Override methods. The method onChildAdded returns every child-nodes of the node when called first time (i.e. on activity start).
Put attention - maybe you will need to slightly change the reference to DB (trim back one hierarchy level), to point to the parent node. If you expanded snapshot of your DB structure, I can look.
Here is updated code (sorry is made any typo - I couldn't test it as have no your DB :)
mDatabaseReference.child("Users").child(mUser.getUid()).child("Posts").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
arrayOfQuestionForms.clear();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
QuestionForm tempQuestionForm = postSnapshot.getValue(QuestionForm.class);
arrayOfQuestionForms.add(tempQuestionForm);
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});