You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:用户关闭App时删除Firebase未完成注册数据

Hey there, let's tackle this problem together! The issue you're facing is that onDestroyView() only triggers when the Fragment is explicitly destroyed (like navigating back), but not when the app is closed abruptly—since the system might kill the process without firing Fragment lifecycle methods. Here's how to fix this reliably:

1. Track if the username was successfully set

First, add a boolean flag to your AddUsername fragment to distinguish between completed and incomplete setup. This ensures we only clean up accounts where the user didn't finish setting a username:

private boolean isUsernameSet = false;

Update this flag to true once the user successfully saves their valid username (add this inside your username validation logic):

// Inside checkUsernameAlreadyExist, after confirming the username is available
mDatabase.child("users").child(user.getUid()).child("username").setValue(getUsername)
    .addOnSuccessListener(aVoid -> {
        isUsernameSet = true;
        // Proceed to the next screen as usual
        fragmentComm.toSetName();
    });

2. Use ProcessLifecycleOwner to detect app exit/background

To catch when the app is closed or sent to the background, we'll use Android's ProcessLifecycleOwner, which listens to the entire app's lifecycle (not just individual fragments/activities).

First, ensure you have the required dependency in your module-level build.gradle:

implementation "androidx.lifecycle:lifecycle-process:2.6.2"

Then, register a lifecycle observer in your AddUsername fragment:

private LifecycleObserver appLifecycleObserver;

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    
    appLifecycleObserver = new DefaultLifecycleObserver() {
        @Override
        public void onStop(@NonNull LifecycleOwner owner) {
            super.onStop(owner);
            // Check if app is moving to background AND username wasn't set
            if (!isUsernameSet && !owner.getLifecycle().isCurrentStateAtLeast(Lifecycle.State.STARTED)) {
                cleanupIncompleteAccount();
            }
        }
    };
    
    ProcessLifecycleOwner.get().getLifecycle().addObserver(appLifecycleObserver);
}

// Centralized cleanup method to delete database entry and user account
private void cleanupIncompleteAccount() {
    if (user == null) return;
    
    // First delete the database entry, then the user account
    mDatabase.child("users").child(user.getUid()).removeValue()
        .addOnSuccessListener(aVoid -> {
            user.delete().addOnCompleteListener(task -> {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Incomplete user account deleted successfully");
                } else {
                    Log.e(TAG, "Failed to delete user account", task.getException());
                }
            });
        })
        .addOnFailureListener(e -> {
            Log.e(TAG, "Failed to delete database entry", e);
            // Even if database delete fails, try to remove the user account
            user.delete();
        });
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    // Remove observer to avoid memory leaks
    ProcessLifecycleOwner.get().getLifecycle().removeObserver(appLifecycleObserver);
    
    // Handle back navigation case (redundant but adds safety)
    if (!isUsernameSet) {
        cleanupIncompleteAccount();
    }
}

3. Fix your original code's operation order

Your initial code called mAuth.signOut() before deleting the user, which is risky—once signed out, the user object may lose permission to call delete(). The cleanupIncompleteAccount method above fixes this by deleting the database entry first, then the user account. There's no need to sign out manually, since deleting the user automatically invalidates their session.

4. Explicitly handle back button (optional)

For more control over back navigation, use OnBackPressedCallback instead of relying on onDestroyView:

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    
    requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            cleanupIncompleteAccount();
            // Navigate back to SignUp fragment
            requireActivity().getSupportFragmentManager().popBackStack();
        }
    });
    
    // ... rest of your onViewCreated code
}

This approach ensures that whether the user presses back or closes the app, incomplete accounts get cleaned up properly.

内容的提问来源于stack exchange,提问作者Kasian Blithe Caballero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:06:44