Firebase load data onCreate() - android

I want to load a dataset oncreate().
Since the data will not change, I use the one time loader:
protected void onCreate(Bundle savedInstanceState) {
....
mDatabase.child("users").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentUser = dataSnapshot.getValue(Users.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
String name = currentUser.getName();
....
}
But the data is not available, the getName function is called on a null object.
How can I get the data onCreate()?

The problem is that Firebase uses an asynchronous listeners to a database reference, and you set name variable with null currentUser.
Try to declare the name var as final and before the listener, and set his value inside the firebase callback.
Update:
an example how to implement and use an "override" function:
final String name;
mDatabase.child("users").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentUser = dataSnapshot.getValue(Users.class);
name = currentUser.getName();
fooFunc(name); // calling a function with data
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
#Override
public void fooFunc(String name) {
// name != null, when called from the the callback
// dont forget to check it
}

Related

Intent is not passing on single click with loading firebase data

In my code, when user click on a button, it calls a function.In that function data has to loaded from firebase. On successful of loading, Intent will pass to another activity with some putExtra data.
SharedPreferences pref = getApplicationContext().getSharedPreferences("My_pref", MODE_PRIVATE);
choice = pref.getInt("language", 0);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(choice==1)
{
firebase("param1","param2","English");
}
if(choice==2)
{
firebase("param1","param2","hindi");
}
}
});
public void firebase(String files,String titles,String language)
{
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(language);
DatabaseReference myRef1=myRef.child(titles);
DatabaseReference myRef2=myRef.child(files);
myRef1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_title = dataSnapshot.getValue(String.class);
t=1;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
myRef2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_file= dataSnapshot.getValue(String.class);
f=1;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
if(t==1&&f==1) {
Intent i = new Intent(getApplicationContext(), caf.class);
i.putExtra("titles", language_title);
i.putExtra("files", language_file);
startActivity(i);
}
}
But in that case intent is only passing when I click twice.In single click Intent is not passing. where is the problem?
Any code that needs data from the database, needs to be inside the onDataChange method or be called from there.
See my answer here for a longer explanation: getContactsFromFirebase() method return an empty list.
Since you have two onDataChange methods, you'll need to make sure both have been called before starting the new activity. Using some simple flag variables to track state, you can do that with:
Declare two member fields:
boolean isTloaded, isFLoaded;
And then
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(language);
DatabaseReference myRef1=myRef.child(titles);
DatabaseReference myRef2=myRef.child(files);
isTloaded = false;
isFloaded = false;
myRef1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_title = dataSnapshot.getValue(String.class);
t=1;
isTloaded = true;
if (isTloaded && isFloaded) {
startCafActivity();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
myRef2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_file= dataSnapshot.getValue(String.class);
f=1;
isFloaded = true;
if (isTloaded && isFloaded) {
startCafActivity();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
And then finally:
private void startCafActivity() {
if(t==1&&f==1) {
Intent i = new Intent(getApplicationContext(), caf.class);
i.putExtra("titles", language_title);
i.putExtra("files", language_file);
startActivity(i);
}
}
It might be even simpler, but I had a hard time figuring out your application logic due to the variable names. Consider using more descriptive named than t, f and caf to make it easier for others (and your future self) to understand what each part of the code does in isolation.

Android Firebase addListenerForSingleValueEvent pass value

I have button in a fragment, which upon click should check if data exists in firebase db or not. Below is the function in a separate class file that will be called on button click in an async task.
How can I return boolean value true/false from addListenerForSingleValueEvent back to the fragment async task?
void checkDataExists(final String mobile){
DatabaseReference fireDBRef = FirebaseDatabase.getInstance().getReference(context.getString(R.string.app_name);
fireDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String mob =
String.valueOf(dataSnapshot.child(context.getString(R.string.tracked_mobile))
.getValue());
//compare the strings mobile
boolean match = mobile.equals(mob);
// return match value to fragment to update the view.
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.w(TAG + "/checkDataExists","Data read from DB failed: " + databaseError.getMessage());
}
});
}
I also have case like this, and I create my own callback like:
public interface IMyCallback {
void onSuccess(boolean isExist);
void onFailure(String error);
}
now when I call function checkDataExists it looks like:
checkDataExists(mobile, new ISingUpCallback() {
#Override
public void onSuccess(boolean isExist) {
}
#Override
public void onFailure(String error) {
}
});
And in your check you need to make changes like:
void checkDataExists(final String mobile, final IMyCallback callback){
DatabaseReference fireDBRef = FirebaseDatabase.getInstance().getReference(context.getString(R.string.app_name);
fireDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String mob =
String.valueOf(dataSnapshot.child(context.getString(R.string.tracked_mobile))
.getValue());
//compare the strings mobile
boolean match = mobile.equals(mob);
// return match value to fragment to update the view.
callback.onSuccess(match);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
callback.onFailure(databaseError.getMessage());
}
});
}

getting multiple node in firebase

I got problem with my app that i develop using Android Studio and Firebase.
I want to get data from firebase and compare it with another data from firebase and show it in a listview.
The data that i want, is the string in the follow image.
The problem here is, I dont know how to get multiple string from the database.
This is the code that i having been trying:
public class testActivity extends AppCompatActivity {
private DatabaseReference applydatabase;
String app;
String applied;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
app = getIntent().getExtras().getString("id");
applydatabase = FirebaseDatabase.getInstance().getReference().child("Apply").child(app).child("Applyid");
applydatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
applied = dataSnapshot.getValue().toString();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
You can use DataSnapshot.getChildren() to get the child nodes and loop over them.
applydatabase = FirebaseDatabase.getInstance().getReference().child("Apply").child(app).child("Applyid");
applydatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String first = null, second = null;
for (DataSnapshot child: dataSnapshot.getChildren()) {
if (first == null) {
first = child.getValue(String.class);
} else if (second == null)
second = child.getValue(String.class);
}
}
// TODO: compare first and second
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});

When I nest two value event listeners, do they both run asynchronously or the the lower one waits for the higher one to be completed?

I am working on an android application, using Firebase and Android Studio.
I had issues with retrieving values with value event listeners because i couldnt set values inside the onDataChange method to be used else where because it runs asynchronously, so the listener may or may not have completed its purpose before the value is needed outside. So I solved this issue by putting whatever needs to be done inside the onDataChange method(real life saver!).
But
the value i am retrieving in the onDataChange also happens to be needed inside another listener. like this
DatabaseReference mLastnameReference = rootRef.child("users").child("12345678").child(phone);
ValueEventListener lastnameListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
try {
yourid = dataSnapshot.getValue().toString();
}
catch (NullPointerException e){
//handle user not registered here
}
DatabaseReference mBioReference = FirebaseDatabase.getInstance().getReference().child("users").child(yourid).child("status");
ValueEventListener bioListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Status = dataSnapshot.getValue().toString();
mTextField = (TextView) findViewById(R.id.status);
mTextField.setText(Status);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mBioReference.addValueEventListener(bioListener);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mLastnameReference.addValueEventListener(lastnameListener);
the value im waiting for in the first listener is yourid . I also need that value to get a database reference to the node for the nested listener to be executed. But when i ran this code, both of them appear to be running at the same time. Hence, getting a null exception on the nested database reference, because the yourid appears to not have been retrieved at that time.
In the code you shared, the value of users/12345678/$phone will be read before you attach the nested listener. But the value might be null, which you code doesn't handle correctly:
DatabaseReference mLastnameReference = rootRef.child("users").child("12345678").child(phone);
ValueEventListener lastnameListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String id = String.valueOf(dataSnapshot.getValue());
if (id != null) {
DatabaseReference mBioReference = FirebaseDatabase.getInstance().getReference().child("users").child(id).child("status");
ValueEventListener bioListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Status = dataSnapshot.getValue(String.class);
mTextField = (TextView) findViewById(R.id.status);
mTextField.setText(Status);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
};
mBioReference.addValueEventListener(bioListener);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
};
mLastnameReference.addValueEventListener(lastnameListener);
I've replaced the try-catch with a regular if and made id a local variable, which removes the chance that some other part of your code sets it to null. It also throw exceptions in onCancelled now, since it's best to not ignore those authorization problems.

Load two depending datasets

I load two datasets in the onCreate function of my activity:
protected void onCreate(Bundle savedInstanceState) {
....
mDatabase.child("users").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentUser = dataSnapshot.getValue(Users.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
mDatabase.child("events").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
...
event = dataSnapshot.getValue(Events.class);
if (event.getHost().equals(currentUser.getId()) {
....
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
String name = currentUser.getName();
....
}
So I need some data of the users dataset in the events dataset. Most of the time this works, but since they are called async, they are sometimes loaded in the wrong order. So events is loaded before users is loaded.
How can I fix this?
If you want to guarantee the order, you can nest the loading of the events into the callback for the user:
String name;
mDatabase.child("users").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
currentUser = dataSnapshot.getValue(Users.class);
name = currentUser.getName();
mDatabase.child("events").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
...
event = dataSnapshot.getValue(Events.class);
if (event.getHost().equals(currentUser.getId()) {
....
}
}
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
}
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
The nesting becomes a bit deep, but if you pull the loading of events into a separate (named) method, that'll typically get better again.

Categories

Resources