【问题标题】:AsyncTask not returning value in timeAsyncTask 没有及时返回值
【发布时间】:2018-07-13 18:33:25
【问题描述】:

我有一个注册活动,它应该检查 Firebase 实时数据库中是否存在特定节点。如果是,则通知用户,否则继续注册过程。我正在使用 AsyncTask 进行检查,但问题是当 AsyncTask 返回 value 时,我的主线程已经执行了其余部分。我正在使用 AsyncTask.execute().get() 来获取应该暂停主线程但它不起作用的值。

注册活动

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();

数据库检查任务

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;
    }
}

调试日志

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

在调试日志中查看 ROLL_MATCHED 是如何在执行默认情况后实现的,而它应该在进入 switch case 语句之前实现。

【问题讨论】:

  • 异步任务将在与主线程不同的单独线程上执行进程,因此主线程将继续执行其代码。因此,您必须在主线程上运行的 AsyncTask 的 onPostExecute() 方法中从 Firebase DB 获取数据后检查特定节点是否存在,有关 AsyncTask 的更多信息,请参阅此链接:stackoverflow.com/a/25647882/6726650

标签: android firebase-realtime-database android-asynctask


【解决方案1】:

AsyncTask 在与主线程不同的单独线程上执行其代码,因此主线程将继续执行其代码,而 AsyncTask 将在单独的线程上运行其代码。您必须等到 AysncTask 的 doInBackground() 方法完成其执行,然后才能继续进行 SignUp 或其他(视情况而定)。因此,您必须将代码转移到在主线程上运行的onPostExecute()。要了解有关 AsyncTask 及其方法的更多信息,请参阅this注意:在 AsyncTask 完成执行之前不能暂停主线程,但可以等到 AsyncTask 生成结果,请参阅this

【讨论】:

    【解决方案2】:

    将您的开关块移动到onPostExecute() 中的DatabaseCheckTask

    @Override 
    protected void onPostExecute(Integer integer)
     { 
    super.onPostExecute(integer);
     //move your switch code here 
    }
    

    【讨论】:

      【解决方案3】:

      您必须按照onPostExecute() 方法的其他答案中的建议移动您的代码,否则您无法知道何时获得结果,因为它在单独的线程上运行。 因此,您可以执行以下示例:

      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);
      
      }
      

      您必须使用WeakReference 来避免泄漏,然后您可以控制 ProgressBar 的可见性(progressBar ref)、显示 Toast 和获取 SharedPreferences(Activity ref)。

      我将ProgressDialog 替换为ProgressBar,因为它在API level 26 中已被弃用

      最后,您可以像下面这样使用您的 AsyncTask:

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

      更新:

      但这里的问题是,在您的 AsyncTask doInBackground() 中,您只是添加了侦听器,它也在单独的线程上运行,这就是为什么即使您在 onPostExecute() 中添加代码,它也是不工作(事件尚未触发)。

      解决方案是将每个“case”语句代码移动到侦听器中的适当位置。 (您不需要 AsyncTask)。只需创建一个以roll_no 作为参数并添加监听器的函数

      【讨论】:

      • 做了这个,还是一样的问题。但是为了检测问题,我在之前的代码中添加了一个额外的案例,发现检查节点是否存在的方法暂时没有返回值。查看这里的代码 - AsyncTaskdebug 。请看一下。 Pastebin 因为我不想编辑原始代码。
      • 谢谢!我不知道侦听器在单独的线程上运行。这就解释了这个问题。
      猜你喜欢
      • 2013-10-31
      • 1970-01-01
      • 2019-07-20
      • 1970-01-01
      • 1970-01-01
      • 2018-01-02
      • 2018-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多