0

MyApplication purpose: Activity class that 1) Reads firebase db, 2) Updates class member variables based on data what was read from firebase db, 3) Evaluate credentials based on class member variables that were updated from the values inside the firebase db

public MyApplication extends AppCompatActivity implements FireBaseDataListener{
private static int N; /* number of firebase keys *//* static to be accessed in db read */
private Button Nbtn;
private FirebaseDataBase mFirebaseDataBase;
private FirebaseReference mFirebaseReference;
... instantiated class member variables in @OnCreate...
... create setters and getters and add listeners ...

public void processing(){
 /* here is the issue, normally i would think this.N updates
    before checkCredentials is called. wrong! since the asynchronous
    design of firebase db i'm perplexed what to do in order to get
    this.N to update before checkCredentials() is called.
    due to this in checkCredentials i get N==0 not N>0.
    however, i peek at the log.d printout and see the value updates
    so i trace the program and see checkCredentials evaluates 
    way before readNfromFireBase is done. */
 Log.d.("...", "before firebase read");
 readNfromFireBase(this);
 Log.d.("...", "before checkCredentials");
 checkCredentials();
}

public void checkCredentials(){
 Log.d.("...", "inside checkCredentials");
 if(this.N == 0) { empty firebase launches dialog warning no keys }
 elseif(this.N > 0) { Log.d("log#1", "N: " + N); }
 else { Log.d("log#0", "N: " + N + " this should never happen but just in case.") }
}

public void readNfromFireBase(FireBaseDataListener listener){
listener.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    ... for each loop to iterate over the data
    /* before sending data to callback i print and verify the number
       of keys is correct, so i know i have the correct data */
    Log.d.("...", "before listener");
    this.N = listener.onKeyCallback(data); // WARNING#0 this.N (i'm aware)
    Log.d.("...", "after listener");
  }
  @Override
  public void onCancelled(DatabaseError databaseError) {
    System.out.println("The read failed: " + databaseError.getCode());
  }
}

/* Override for FireBaseDataListener */

@Override public onKeyCallBack(int nKeys){
 Log.d.("...", "inside callback listener");
 this.N = nKeys; // WARNING#1 this.N (i'm aware)
 return this.N;
}

} //end of my application class

interface FireBaseDataListener{
void onKeyCallback(int nKeys);
} // end of interface

notes on WARNING#0 & WARNING#1 - for learning purpose can someone shed some light which is more appropriate, to update the value inside the listener routine or inside the override callback, in other words, where should i update, inside warning#0 or warning#1?

sample execution - weird but it goes like this

"before firebase read"
"before listener"
"before checkCredentials"
"inside checkCredentials"
"inside callback listener"
"after listener"

sample execution - this is how it should be, but it's not.

"before firebase read"
"before listener"
"inside callback listener"
"after listener"
"before checkCredentials"
"inside checkCredentials"

In short, the code is evaluating future functions before the database read returns.

Firouziam
  • 777
  • 1
  • 9
  • 31
EvOlaNdLuPiZ
  • 600
  • 1
  • 4
  • 21
  • i've tried using new Handler().postDelayed(new Runnable() ... , delaySecond) , no luck. i know i'm getting the right values because after i press the button a few times the class member variables *do* update to what they're expected. – EvOlaNdLuPiZ Jul 16 '18 at 18:46
  • 1
    Firebase APIs are asynchronous, so they do not pause the app to wait for results. See [this blog post](https://medium.com/google-developers/why-are-firebase-apis-asynchronous-callbacks-promises-tasks-e037a6654a93) for an explanation of why. If you need the data from the callback before calling checkCredentials, then call checkCredentials in the callback. – Jen Person Jul 16 '18 at 22:36
  • Great article, so there isn't a way apparently, the only way is to restructure code? – EvOlaNdLuPiZ Jul 16 '18 at 22:44
  • What would be the approriate place to update the 'this.N' class member? – EvOlaNdLuPiZ Jul 16 '18 at 22:47
  • 1
    Check [this](https://stackoverflow.com/questions/47847694/how-to-return-datasnapshot-value-as-a-result-of-a-method/47853774) or [this](https://stackoverflow.com/questions/51355443/not-able-to-access-inner-class-data-from-outer-class-in-android/51359832) out. – Alex Mamo Jul 17 '18 at 09:42
  • wonderful. simply following the advice, moving the method to be called inside the asynchronous task solved the issue. – EvOlaNdLuPiZ Jul 17 '18 at 14:24
  • while this is a bit late, i'd like to mention that this approach did let me get the data i needed because firebase had a chance to "catchup" in synchronizing the data, by doing a similar to clock cycles, "NO-OP" (empty function accessing firebase). i found a much better way, to use callback. – EvOlaNdLuPiZ Jan 31 '19 at 22:35

1 Answers1

0

callback will work much better in accessing firebase data.

/* callback interface */
public interface FireBaseCallBack {
 void doCallBack(SomeClass sc);
}

public class FireBaseHandler{
 private FirebaseDatabase mFirebaseDatabase;
 private DatabaseReference mFireBaseReference;

 public FireBaseHandler(){
  this.mFirebaseDatabase = FirebaseDatabase.getInstance();
  this.mFireBaseReference = mFirebaseDatabase.getReference();

 public void operationOnFireBase(final FireBaseCallBack callback){
  mFireBaseReference.addListenerForSingleValueEvent(new ValueEventListener(){
   @Override void onDataChange(@NonNull DataSnapshot dataSnapshot){
    SomeClass temp = new SomeClass();
    temp.set/get on data in firebase
    callback.doCallBack(temp);
   }
  }
 }
}

public class SomeClass{
 private FireBaseHandler mFireBaseHandler;
 void someFunction(){
  mFireBaseHandler.operationOnFireBase(new FireBaseCallBack()){
    @Override void doCallBack(SomeClas sc){
     ...
    }
   });
  }
 }
}
EvOlaNdLuPiZ
  • 600
  • 1
  • 4
  • 21