0

I've a SignUp activity which is supposed to check if a particular node exists in the Firebase realtime Database. If it does then notify the user else proceed with the sign up process. I'm using an AsyncTask to do that check but the problem is that when AsyncTask returns the value , my main thread has already been executed the rest. I'm using AsyncTask.execute().get() to fetch value which is supposed to pause the main thread but it is not working.

SignUp Activity

int userRegistered;
firebaseDatabase = FirebaseDatabase.getInstance();
databaseReference = firebaseDatabase.getReference("students");
ProgressDialog progressDialog = 
ProgressDialog.show(context_signup,"Registering","Wait");
Log.d("signup","DB_CHECKER_STARTING");
try{
    DatabaseCheckTask databaseCheckTask = new DatabaseCheckTask();
    userRegistered = databaseCheckTask.execute(roll_no).get();
}
catch (InterruptedException | ExecutionException e){
    userRegistered = 0;
}

switch (userRegistered){
    case 1:
        Log.d("signup","SIGNUP");
        SignUpTask signUpTask = new SignUpTask();
        signUpTask.execute(first_name,last_name,roll_no,phone_no,serial);
      sharedPreferences.edit().putBoolean(Constants.FIRST_RUN,false).apply();
       break;
    case 2:
        Log.d("signup","NOSIGNUP");
        Toast.makeText(context_signup,"Student with this Roll number is 
        registered already..",Toast.LENGTH_LONG).show();
        break;
    case 3:
        Log.d("signup","DATABASE_ERROR");
        Toast.makeText(context_signup,"Error connecting to database! Please try again after some time.",Toast.LENGTH_LONG).show();
        break;
    default:
        Log.d("signup","DEFAULT_CASE");
        Toast.makeText(context_signup,"Slow",Toast.LENGTH_LONG).show();
    }
Log.d("signup","DISMISS");
progressDialog.dismiss();

DatabaseCheckTask

public class DatabaseCheckTask extends AsyncTask<String,Integer,Integer>{
int isRegistered;
@Override
protected Integer doInBackground(String... strings) {
    DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("students");
    databaseReference.child(strings[0]).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            if(dataSnapshot.exists()){
                Log.d("db_checker","ROLL_MATCHED");
                isRegistered = 2;
            }
            else {
                Log.d("db_checker","ROLL_NOT_MATCHED");
                isRegistered = 1;
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            Log.d("checker","database error");
            isRegistered = 3;
        }
    });
    return isRegistered;
    }
}

Debug Log

D/signup: DB_CHECKER_STARTING
D/signup: DEFAULT_CASE
D/signup: DISMISS
D/NetworkSecurityConfig: No Network Security Config specified, using platform 
default
V/FA: Recording user engagement, ms: 5473
V/FA: Connecting to remote service
V/FA: Activity paused, time: 785707071
D/FA: Logging event (FE): user_engagement(_e), 
Bundle[{firebase_event_origin(_o)=auto, engagement_time_msec(_et)=5473, 
firebase_screen_class(_sc)=SignUp, 
firebase_screen_id(_si)=6624230925954519568}]
V/FA: Connection attempt already in progress
V/FA: onActivityCreated
I/art: Do full code cache collection, code=245KB, data=251KB
I/art: After code cache collection, code=221KB, data=188KB
D/FA: Connected to remote service
V/FA: Processing queued up service tasks: 2
V/FA: onActivityCreated
D/FA: Logging event (FE): screen_view(_vs), 
Bundle[{firebase_event_origin(_o)=auto, firebase_previous_class(_pc)=SignUp, 
firebase_previous_id(_pi)=6624230925954519568, 
firebase_screen_class(_sc)=SignUp, 
firebase_screen_id(_si)=6624230925954519569}]
V/FA: Activity resumed, time: 785707379
 D/OpenGLRenderer: endAllActiveAnimators on 0x9a56f880 (InsetDrawable) with handle 0x98617430
 D/db_checker: ROLL_MATCHED

See in the Debug Log how ROLL_MATCHED is achieved after executing the default case while it was supposed to get achieved before even entering the switch case statement.

  • Async Task will execute the process on a separate thread that is different from the main thread so the main thread will keep on executing its code. So you have to check if the particular node exists after you have fetched the data from the Firebase DB in the onPostExecute() method of AsyncTask which runs on the main thread, for more insight on AsyncTask see this link: https://stackoverflow.com/a/25647882/6726650 – Sudhanshu Vohra Jul 13 '18 at 07:45

3 Answers3

1

Move your switch block in onPostExecute() inside your DatabaseCheckTask

@Override 
protected void onPostExecute(Integer integer)
 { 
super.onPostExecute(integer);
 //move your switch code here 
}
Hemant Parmar
  • 3,924
  • 7
  • 25
  • 49
prashant17
  • 1,520
  • 3
  • 14
  • 23
1

AsyncTask executs its code on separate thread which is different from main thread and hence the main thread will keep executing its code while the AsyncTask will run its code on a separate thread. You will have to wait till the AysncTask's doInBackground() method completes its execution and then only proceed with SignUp or other (depending upon the case). So you will have to shift your code to the onPostExecute() which runs on the main thread. To know more about AsyncTask and its methods see this. Note: You cannot pause the main thread till AsyncTask completes its execution but you can wait till the AsyncTask generates its result, see this.

Sudhanshu Vohra
  • 1,345
  • 2
  • 14
  • 21
0

You have to move your code, as suggested in the other answers to the onPostExecute() method, otherwise you can't know when to get the result because it is running on a separate thread. So you can do something like the example below :

public class DatabaseCheckTask extends AsyncTask<String,Integer,Integer>{
int isRegistered;
/* use WeakReference to avoid leaks */
private WeakReference<ProgressBar> progressBarRef;
private WeakReference<Activity> activityRef;

public DatabaseCheckTask(ProgressBar progressBar, Activity activity){

    this.progressBarRef = new WeakReference<>(progressBar);
    this.activityRef = new WeakReference<>(activity);
}

@Override
protected void onPreExecute() {
    super.onPreExecute();
    // get the progressBar
    ProgressBar progressBar = progressBarRef.get();
    // show the progressBar
    progressBar.setVisibility(View.VISIBLE);
}

@Override
protected void onPostExecute(Integer userRegistered) {
    super.onPostExecute(userRegistered);
    Activity mActivity = activityRef.get();
    switch (userRegistered){
    case 1:
        Log.d("signup","SIGNUP");
        SignUpTask signUpTask = new SignUpTask();
       /* Assuming you pass these variables to you asynctask.execute() and stock them 
       globally */
        signUpTask.execute(first_name,last_name,roll_no,phone_no,serial);
       /* you have a reference to the activity, so you can get sharedPrefereces doing 
       mActivity.getSharedPref.... */
       sharedPreferences.edit().putBoolean(Constants.FIRST_RUN,false).apply();
       break;
   case 2:
       Log.d("signup","NOSIGNUP");
       Toast.makeText(mActivity,"Student with this Roll number is 
       registered already..",Toast.LENGTH_LONG).show();
       break;
   case 3:
       Log.d("signup","DATABASE_ERROR");
       Toast.makeText(mActivity,"Error connecting to database! Please try again 
       after some time.",Toast.LENGTH_LONG).show();
       break;
   default:
       Log.d("signup","DEFAULT_CASE");
       Toast.makeText(mActivity,"Slow",Toast.LENGTH_LONG).show();
   }
       ProgressBar progressBar = progressBarRef.get();
       progressBar.setVisibility(View.INVISIBLE);

}

You have to use WeakReference to avoid leaks and then you can controle your ProgressBar visibility (the progressBar ref), showing Toasts and getting SharedPreferences (The activity ref).

I replaced the ProgressDialog by ProgressBar because it is deprecated in API level 26

And finally, you can use your AsyncTask like below:

DatabaseCheckTask task = new DatabaseCheckTask(progressBar, MainActivity.this);
task.execute(/* Your variables);

UPDATE :

But the problem here, in your AsyncTask doInBackground(), you are just adding listener, wich is running on separate thread too, that is the reason why even if you add your code in onPostExecute(), it is not working (the event is not fired yet).

A solution is to move each "case" statement code to the adequate placement in your listener. (You do not need AsyncTask). Just create a function that takes roll_no as parameter and that add the listener

E.Abdel
  • 1,992
  • 1
  • 13
  • 24
  • Did this, still the same problem. However to detect problem I added an extra case to my previous code and found that the method to check if node exists is not returning value in the time being.Check out the code here - [AsyncTask](https://pastebin.com/b0AhvJc7) and the [debug](https://pastebin.com/U1vZzDT1) . Please take a look. Pastebin because I don't want to edit original code. – Shahnawaz Ansari Jul 13 '18 at 10:10
  • 1
    Thanks! I wasn't aware that listener was running on a separate thread. That explains the issue. – Shahnawaz Ansari Jul 13 '18 at 15:57