【问题标题】:Data Retrieval from Firestore is erratic从 Firestore 检索数据不稳定
【发布时间】:2020-07-13 06:50:52
【问题描述】:

我正在开发一个执行以下操作的 Android 应用:

  1. 在应用启动时,它会使用 AuthStateListener 检查用户是否已登录。
  2. 如果有用户登录,它会从 Firestore 中检索数据。用户数据存储在我使用以下命名法命名的文档中:“用户”+用户的_email_ID。 例如,如果用户的电子邮件 ID xyz@gmail.com,他的数据将存储在名为:User xyz@gmail.com。 所有文档都在名为“Users”的集合中。
  3. 如果用户数据文档中的所有字段均为空/空,应用程序会打开一个 Activity,要求他/她填写所有详细信息。否则,它将用户带到主页(如果用户是学生,则为StudentMainActivity,如果用户是教授,则为ProfessorMainActivity)。

来解决我的问题:

检查字段是否为空的代码块有一些不稳定和不可预测的行为。我不确定这是基于 Firestore 的问题,还是数据检索发生在不同线程上的问题。

我检查了 Firestore 数据库,发现所有字段都已填写。但是,当用户(已经登录)启动应用程序时,应用程序知道它是同一个用户(即他没有被提示登录,因为 AuthStateListener 完成了它的工作),而不是被重定向到 StudentMainActivity 或 ProfessorMainActivity (主屏幕),他被要求再次填写他的详细信息。

更令人困惑的是,这个错误并不总是发生。有时应用会执行预期的操作,即将用户带到主屏幕,但下次启动应用时,他会再次被带到要求他输入详细信息的活动。

源代码:

LoginActivity.java(仅相关部分)

    //AuthStateListener is in onCreate
    authStateListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            FirebaseUser user = firebaseAuth.getCurrentUser();
            if (user != null){
                UIDEmailID = user.getEmail();
                updateUI(user);
            }
            else{
                updateUI(null);
            }
        }
    };

private void updateUI(FirebaseUser user){
    // Update UI after login
    if (user != null) {
        Toast.makeText(LoginActivity.this, "User " + UIDEmailID, Toast.LENGTH_LONG).show();
        db.collection("Users").document("User " + UIDEmailID).get()
                .addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                    @Override
                    public void onSuccess(DocumentSnapshot documentSnapshot) {
                        if (documentSnapshot.get("department") != null ||       // if any
                        documentSnapshot.get("phoneNumber") != null ||          // field in
                        documentSnapshot.get("name") != null ||                 // Firestore is
                        documentSnapshot.get("studentSemester") != null ||      // non-null then
                        documentSnapshot.get("dateOfBirth") != null ||          // proceed to
                        documentSnapshot.get("university") != null) {           // further activities
                            if (documentSnapshot.get("userType") == "Lecturer/ Professor") {
                                Intent intent = new Intent(LoginActivity.this, ProfessorMainActivity.class);
                                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                startActivity(intent);
                            }
                            else {
                                Intent intent = new Intent(LoginActivity.this, StudentMainActivity.class);
                                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                startActivity(intent);
                            }
                        } else {
                            Toast.makeText(LoginActivity.this, "We need some additional details before we go ahead.", Toast.LENGTH_SHORT).show();
                            Intent intent = new Intent(LoginActivity.this, GFBDetailsActivity.class);
                            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivity(intent);
                        }
                    }
                }).addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        Toast.makeText(LoginActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                    }
                });
    }

}

很抱歉这个问题太长了;我只是试图使它具有超级描述性。一些帮助将不胜感激。

P.S. 我认为这是一个涉及使用多线程的问题的原因是,每当应用程序按预期运行(即,将用户带到主屏幕)时,“我们需要一些在我们继续之前提供更多详细信息。”也出现。如果您查看代码(最后一个“else”块),您会意识到它完全位于单独的条件块中,因此如果主屏幕(位于另一个条件块中)显示,甚至不应该显示起来。

编辑 1:

我附上了与问题相关的屏幕截图。忽略平淡的用户界面:P

这是预期的(位于第二个“else”块下)。它应该仅在用户第一次登录时显示,即没有将他的数据存储在 Firestore 文档中。

背景是 StudentMainActivity(在嵌套的“else”内)。但是,即使是 Toast 也会显示(它完全属于一个单独的块)。

【问题讨论】:

  • 你能到达 onSucess() 方法的第一个 if 吗?还是直接跳转到else?请在变量用户的 if 之前打印并分享变量用户有什么?
  • 查看您的代码,我发现可能是代码错误,要获取您当前的用户数据,您必须使用以下代码: FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();如果您仍然遇到相同的问题,请您输入以前的代码并重试吗?请让我知道它是否有效。
  • 我已经分析了你的代码,它似乎是正确的,为了进一步的故障排除,你能否在 onSucess 方法中的第一个 if 之前请 System.out.prinln(documentSnapshot) 查看来自 firestore 的数据是否是被正确检索?能否请您分享您的 manifest.xml 文件,也请您分享您的 Student、Professor 和 GFBDetailsActivity 的文件(代码)?活动打开错误似乎有问题。
  • 感谢分享您的代码,我一直在查看您的代码。我想我能够找到问题所在。也许您查询错误,这就是 documentSnapshot 为空且没有显示的原因。您能否告诉我您的 Firestore 的结构,我的意思是在这里我想看看您的文档 ID 是如何命名的?查询是空的,因为可能 UIDEmailID 是空的,请敬酒或在 onAuthStateChanged 中打印 UIDEmailID 并查看它是什么?
  • 你也可以这样查询一个文档: db.collection(COLLECTION_NAME) .whereEqualTo(DOCUMENT_ID, documentId) .get() .addOnCompleteListener(task -> { if (task.isSuccessful()) { if (task.getResult().getDocuments().size() > 0) // 这是你的文档,id } });

标签: java android firebase google-cloud-firestore firebase-authentication


【解决方案1】:

所以事实证明 Firestore 并没有(完全)有错。

Android 应用程序中的每个 Activity 都有一个生命周期,每次运行一个 Activity 时,都会经历一系列复杂的生命周期函数

一个activity的生命周期如下:

已启动 --> onCreate() --> onStart() --> onResume() --> 正在运行 --> onPause() --> onStop () --> onDestroy() --> 完成

我不会偏离每个函数的细节,因为函数名称非常直观且不言自明。

正如您在问题中的代码 sn-p 中看到的,onAuthStateChanged() 在 onCreate() 内部。我在 Firebase 上的文档 ID 的格式为“用户 UIDEmailID”,其中 UIDEmailID 是用户的电子邮件 ID。并且 UIDEmailID 仅在 onAuthStateChanged() 中更新(反过来,在 onCreate() 中),即仅当应用程序关闭并再次打开后重新启动活动时。

因此,我也在 onStart() 中更新了 UIDEmailID,这意味着每次应用程序恢复时,它都会检索用户的电子邮件 ID,随后可以使用该 ID 从 Firestore 检索文档。

此外,根据Nibrass H 的建议,我稍微调整了我的 Firestore 数据检索代码。解决方法如下:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    running = true;
    if (savedInstanceState != null){
        running = savedInstanceState.getBoolean("running");
        wasrunning = savedInstanceState.getBoolean("wasrunning");
    }

    setContentView(R.layout.splash_screen);

    firebaseAuth = FirebaseAuth.getInstance();
    db = FirebaseFirestore.getInstance();

    authStateListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth1) {
            FirebaseUser user = firebaseAuth1.getCurrentUser();
            if (user != null){
                UIDEmailID = user.getEmail();
                updateUI(user);
            } else {
                updateUI(null);
            }
        }
    };
}

@Override
protected void onStart() {
    super.onStart();
    firebaseAuth.addAuthStateListener(authStateListener);
    if (firebaseAuth.getCurrentUser() != null) {
        UIDEmailID = firebaseAuth.getCurrentUser().getEmail();
        updateUI(firebaseAuth.getCurrentUser());
    } else {
        updateUI(null);
    }
}

@Override
protected void onRestart() {
    super.onRestart();
    authStateListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth1) {
            FirebaseUser user = firebaseAuth1.getCurrentUser();
            if (user != null) {
                UIDEmailID = user.getEmail();
                updateUI(user);
            } else {
                updateUI(null);
            }
        }
    };
}

@Override
protected void onPause() {
    super.onPause();
    wasrunning = running;
    running = false;
}

@Override
protected void onResume() {
    super.onResume();
    if (wasrunning){
        running = true;
    }
}

@Override
protected void onStop() {
    super.onStop();
    if (authStateListener != null) {
        firebaseAuth.removeAuthStateListener(authStateListener);
    }
}

private void updateUI(FirebaseUser firebaseUser){
    if (firebaseUser != null){
        Toast.makeText(this, "User " + firebaseUser.getEmail(), Toast.LENGTH_SHORT).show();
        db.collection("Users").document("User " + UIDEmailID).get()
                .addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                    @Override
                    public void onSuccess(DocumentSnapshot documentSnapshot) {
                        if (documentSnapshot.get("userType") != null) {
                            if (documentSnapshot.get("userType").equals("Lecturer/ Professor")){
                                Intent intent = new Intent(SplashScreenActivity.this, ProfessorMainActivity.class);
                                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                finish();
                                startActivity(intent);
                            } else {
                                Intent intent = new Intent(SplashScreenActivity.this, StudentMainActivity.class);
                                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                finish();
                                startActivity(intent);
                            }
                        } else {
                            Toast.makeText(SplashScreenActivity.this, "We need some additional details before we go ahead.", Toast.LENGTH_SHORT).show();
                            Intent intent = new Intent(SplashScreenActivity.this, GFBDetailsActivity.class);
                            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            finish();
                            startActivity(intent);
                        }
                    }
                });
    }
}

【讨论】:

    猜你喜欢
    • 2021-05-21
    • 1970-01-01
    • 2018-08-07
    • 2020-06-22
    • 2019-05-03
    • 2019-09-24
    • 2021-06-16
    • 1970-01-01
    相关资源
    最近更新 更多