Условная навигация,Условная навигация

При разработке навигации для вашего приложения вы можете захотеть перейти к одному пункту назначения вместо другого на основе условной логики. Например, пользователь может перейти по глубокой ссылке к месту назначения, требующему от пользователя входа в систему, или в игре могут быть разные места назначения, когда игрок выигрывает или проигрывает.

Вход пользователя

В этом примере пользователь пытается перейти на экран профиля, требующий аутентификации. Поскольку это действие требует аутентификации, пользователь должен быть перенаправлен на экран входа в систему, если он еще не прошел аутентификацию.

Граф навигации для этого примера может выглядеть примерно так:

поток входа в систему обрабатывается независимо от основного потока навигации приложения.
Рис. 1. Процесс входа в систему обрабатывается независимо от основного потока навигации приложения.

Для аутентификации приложение должно перейти к login_fragment , где пользователь может ввести имя пользователя и пароль для аутентификации. В случае принятия пользователь отправляется обратно на экран profile_fragment . Если он не принят, пользователю сообщается, что его учетные данные недействительны, с помощью Snackbar . Если пользователь возвращается к экрану профиля без входа в систему, он отправляется на экран main_fragment .

Вот граф навигации для этого приложения:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
       
xmlns:app="http://schemas.android.com/apk/res-auto"
       
xmlns:tools="http://schemas.android.com/tools"
       
android:id="@+id/nav_graph"
       
app:startDestination="@id/main_fragment">
   
<fragment
           
android:id="@+id/main_fragment"
           
android:name="com.google.android.conditionalnav.MainFragment"
           
android:label="fragment_main"
           
tools:layout="@layout/fragment_main">
       
<action
               
android:id="@+id/navigate_to_profile_fragment"
               
app:destination="@id/profile_fragment"/>
   
</fragment>
   
<fragment
           
android:id="@+id/login_fragment"
           
android:name="com.google.android.conditionalnav.LoginFragment"
           
android:label="login_fragment"
           
tools:layout="@layout/login_fragment"/>
   
<fragment
           
android:id="@+id/profile_fragment"
           
android:name="com.google.android.conditionalnav.ProfileFragment"
           
android:label="fragment_profile"
           
tools:layout="@layout/fragment_profile"/>
</navigation>

MainFragment содержит кнопку, которую пользователь может нажать, чтобы просмотреть свой профиль. Если пользователь хочет увидеть экран профиля, он должен сначала пройти аутентификацию. Это взаимодействие моделируется с использованием двух отдельных фрагментов, но зависит от общего состояния пользователя. Эта информация о состоянии не является ответственностью ни одного из этих двух фрагментов, и ее более уместно хранить в общей UserViewModel . Эта ViewModel используется совместно фрагментами, ограничивая ее областью действия, которая реализует ViewModelStoreOwner . В следующем примере requireActivity() разрешается в MainActivity , поскольку MainActivity размещается ProfileFragment :

class ProfileFragment : Fragment() {
   
private val userViewModel: UserViewModel by activityViewModels()
   
...
}
public class ProfileFragment extends Fragment {
   
private UserViewModel userViewModel;
   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
       
super.onViewCreated(view, savedInstanceState);
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);
       
...
   
}
   
...
}

Пользовательские данные в UserViewModel предоставляются через LiveData , поэтому, чтобы решить, куда перемещаться, вам следует наблюдать за этими данными. При переходе к ProfileFragment приложение отображает приветственное сообщение, если данные пользователя присутствуют. Если данные пользователя имеют null , вы затем переходите к LoginFragment , поскольку пользователю необходимо пройти аутентификацию, прежде чем он увидит свой профиль. Определите решающую логику в ProfileFragment , как показано в следующем примере:

class ProfileFragment : Fragment() {
   
private val userViewModel: UserViewModel by activityViewModels()

   
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       
super.onViewCreated(view, savedInstanceState)
       
val navController = findNavController()
        userViewModel
.user.observe(viewLifecycleOwner, Observer { user ->
           
if (user != null) {
                showWelcomeMessage
()
           
} else {
                navController
.navigate(R.id.login_fragment)
           
}
       
})
   
}

   
private fun showWelcomeMessage() {
       
...
   
}
}
public class ProfileFragment extends Fragment {
   
private UserViewModel userViewModel;

   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
       
super.onViewCreated(view, savedInstanceState);
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);
       
final NavController navController = Navigation.findNavController(view);
        userViewModel
.user.observe(getViewLifecycleOwner(), (Observer<User>) user -> {
           
if (user != null) {
                showWelcomeMessage
();
           
} else {
                navController
.navigate(R.id.login_fragment);
           
}
       
});
   
}

   
private void showWelcomeMessage() {
       
...
   
}
}

Если пользовательские данные имеют null при достижении ProfileFragment , они перенаправляются в LoginFragment .

Вы можете использовать NavController.getPreviousBackStackEntry() для получения NavBackStackEntry для предыдущего пункта назначения, который инкапсулирует состояние, специфичное для NavController , для пункта назначения. LoginFragment использует SavedStateHandle предыдущего NavBackStackEntry , чтобы установить начальное значение, указывающее, успешно ли пользователь вошел в систему. Это состояние, которое мы хотели бы вернуть, если бы пользователь немедленно нажал кнопку возврата в систему. Установка этого состояния с помощью SavedStateHandle гарантирует, что состояние сохранится даже после смерти процесса.

class LoginFragment : Fragment() {
   
companion object {
        const
val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL"
   
}

   
private val userViewModel: UserViewModel by activityViewModels()
   
private lateinit var savedStateHandle: SavedStateHandle

   
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        savedStateHandle
= findNavController().previousBackStackEntry!!.savedStateHandle
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false)
   
}
}
public class LoginFragment extends Fragment {
   
public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL"

   
private UserViewModel userViewModel;
   
private SavedStateHandle savedStateHandle;

   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);

        savedStateHandle
= Navigation.findNavController(view)
               
.getPreviousBackStackEntry()
               
.getSavedStateHandle();
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false);
   
}
}

Как только пользователь вводит имя пользователя и пароль, они передаются в UserViewModel для аутентификации. Если аутентификация прошла успешно, UserViewModel сохраняет данные пользователя. Затем LoginFragment обновляет значение LOGIN_SUCCESSFUL в SavedStateHandle и извлекает себя из заднего стека.

class LoginFragment : Fragment() {
   
companion object {
        const
val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL"
   
}

   
private val userViewModel: UserViewModel by activityViewModels()
   
private lateinit var savedStateHandle: SavedStateHandle

   
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        savedStateHandle
= findNavController().previousBackStackEntry!!.savedStateHandle
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false)

       
val usernameEditText = view.findViewById(R.id.username_edit_text)
       
val passwordEditText = view.findViewById(R.id.password_edit_text)
       
val loginButton = view.findViewById(R.id.login_button)

        loginButton
.setOnClickListener {
           
val username = usernameEditText.text.toString()
           
val password = passwordEditText.text.toString()
            login
(username, password)
       
}
   
}

   
fun login(username: String, password: String) {
        userViewModel
.login(username, password).observe(viewLifecycleOwner, Observer { result ->
           
if (result.success) {
                savedStateHandle
.set(LOGIN_SUCCESSFUL, true)
                findNavController
().popBackStack()
           
} else {
                showErrorMessage
()
           
}
       
})
   
}

   
fun showErrorMessage() {
       
// Display a snackbar error message
   
}
}
public class LoginFragment extends Fragment {
   
public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL"

   
private UserViewModel userViewModel;
   
private SavedStateHandle savedStateHandle;

   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);

        savedStateHandle
= Navigation.findNavController(view)
               
.getPreviousBackStackEntry()
               
.getSavedStateHandle();
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false);

       
EditText usernameEditText = view.findViewById(R.id.username_edit_text);
       
EditText passwordEditText = view.findViewById(R.id.password_edit_text);
       
Button loginButton = view.findViewById(R.id.login_button);

        loginButton
.setOnClickListener(v -> {
           
String username = usernameEditText.getText().toString();
           
String password = passwordEditText.getText().toString();
            login
(username, password);
       
});
   
}

   
private void login(String username, String password) {
        userViewModel
.login(username, password).observe(viewLifecycleOwner, (Observer<LoginResult>) result -> {
           
if (result.success) {
                savedStateHandle
.set(LOGIN_SUCCESSFUL, true);
               
NavHostFragment.findNavController(this).popBackStack();
           
} else {
                showErrorMessage
();
           
}
       
});
   
}

   
private void showErrorMessage() {
       
// Display a snackbar error message
   
}
}

Обратите внимание, что вся логика, относящаяся к аутентификации, хранится в UserViewModel . Это важно, поскольку ни LoginFragment , ни ProfileFragment не обязаны определять способ аутентификации пользователей. Инкапсуляция вашей логики в ViewModel упрощает ее не только совместное использование, но и тестирование. Если ваша логика навигации сложна, вам следует особенно проверить эту логику посредством тестирования. Дополнительную информацию о структурировании архитектуры вашего приложения на основе тестируемых компонентов см. в Руководстве по архитектуре приложения.

Вернувшись в ProfileFragment , значение LOGIN_SUCCESSFUL , хранящееся в SavedStateHandle можно наблюдать в методе onCreate() . Когда пользователь возвращается к ProfileFragment , будет проверено значение LOGIN_SUCCESSFUL . Если значение false , пользователь может быть перенаправлен обратно в MainFragment .

class ProfileFragment : Fragment() {
   
...

   
override fun onCreate(savedInstanceState: Bundle?) {
       
super.onCreate(savedInstanceState)

       
val navController = findNavController()

       
val currentBackStackEntry = navController.currentBackStackEntry!!
       
val savedStateHandle = currentBackStackEntry.savedStateHandle
        savedStateHandle
.getLiveData<Boolean>(LoginFragment.LOGIN_SUCCESSFUL)
               
.observe(currentBackStackEntry, Observer { success ->
                   
if (!success) {
                       
val startDestination = navController.graph.startDestination
                       
val navOptions = NavOptions.Builder()
                               
.setPopUpTo(startDestination, true)
                               
.build()
                        navController
.navigate(startDestination, null, navOptions)
                   
}
               
})
   
}

   
...
}
public class ProfileFragment extends Fragment {
   
...

   
@Override
   
public void onCreate(@Nullable Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);

       
NavController navController = NavHostFragment.findNavController(this);

       
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
       
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
        savedStateHandle
.getLiveData(LoginFragment.LOGIN_SUCCESSFUL)
               
.observe(navBackStackEntry, (Observer<Boolean>) success -> {
                   
if (!success) {
                       
int startDestination = navController.getGraph().getStartDestination();
                       
NavOptions navOptions = new NavOptions.Builder()
                               
.setPopUpTo(startDestination, true)
                               
.build();
                        navController
.navigate(startDestination, null, navOptions);
                   
}
               
});
   
}

   
...
}

Если пользователь успешно вошел в систему, ProfileFragment отображает приветственное сообщение.

Используемая здесь методика проверки результата позволяет различать два разных случая:

  • Начальный случай, когда пользователь не вошел в систему, и ему будет предложено войти в систему.
  • Пользователь не вошел в систему, поскольку он решил не входить в систему (результат false ).

Различая эти варианты использования, вы можете избежать повторного запроса пользователя на вход в систему. Бизнес-логика для обработки случаев сбоя остается за вами и может включать в себя отображение наложения, объясняющего, почему пользователю необходимо войти в систему, завершение всего действия или перенаправление пользователя в пункт назначения, не требующий входа в систему, как это было в случае с предыдущий пример кода.

,

При разработке навигации для вашего приложения вы можете захотеть перейти к одному пункту назначения вместо другого на основе условной логики. Например, пользователь может перейти по глубокой ссылке к месту назначения, требующему от пользователя входа в систему, или в игре могут быть разные места назначения, когда игрок выигрывает или проигрывает.

Вход пользователя

В этом примере пользователь пытается перейти на экран профиля, требующий аутентификации. Поскольку это действие требует аутентификации, пользователь должен быть перенаправлен на экран входа в систему, если он еще не прошел аутентификацию.

Граф навигации для этого примера может выглядеть примерно так:

поток входа в систему обрабатывается независимо от основного потока навигации приложения.
Рис. 1. Процесс входа в систему обрабатывается независимо от основного потока навигации приложения.

Для аутентификации приложение должно перейти к login_fragment , где пользователь может ввести имя пользователя и пароль для аутентификации. В случае принятия пользователь отправляется обратно на экран profile_fragment . Если он не принят, пользователю сообщается, что его учетные данные недействительны, с помощью Snackbar . Если пользователь возвращается к экрану профиля без входа в систему, он отправляется на экран main_fragment .

Вот граф навигации для этого приложения:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
       
xmlns:app="http://schemas.android.com/apk/res-auto"
       
xmlns:tools="http://schemas.android.com/tools"
       
android:id="@+id/nav_graph"
       
app:startDestination="@id/main_fragment">
   
<fragment
           
android:id="@+id/main_fragment"
           
android:name="com.google.android.conditionalnav.MainFragment"
           
android:label="fragment_main"
           
tools:layout="@layout/fragment_main">
       
<action
               
android:id="@+id/navigate_to_profile_fragment"
               
app:destination="@id/profile_fragment"/>
   
</fragment>
   
<fragment
           
android:id="@+id/login_fragment"
           
android:name="com.google.android.conditionalnav.LoginFragment"
           
android:label="login_fragment"
           
tools:layout="@layout/login_fragment"/>
   
<fragment
           
android:id="@+id/profile_fragment"
           
android:name="com.google.android.conditionalnav.ProfileFragment"
           
android:label="fragment_profile"
           
tools:layout="@layout/fragment_profile"/>
</navigation>

MainFragment содержит кнопку, которую пользователь может нажать, чтобы просмотреть свой профиль. Если пользователь хочет увидеть экран профиля, он должен сначала пройти аутентификацию. Это взаимодействие моделируется с использованием двух отдельных фрагментов, но зависит от общего состояния пользователя. Эта информация о состоянии не является ответственностью ни одного из этих двух фрагментов, и ее более уместно хранить в общей UserViewModel . Эта ViewModel используется совместно фрагментами, ограничивая ее областью действия, которая реализует ViewModelStoreOwner . В следующем примере requireActivity() разрешается в MainActivity , поскольку MainActivity размещается ProfileFragment :

class ProfileFragment : Fragment() {
   
private val userViewModel: UserViewModel by activityViewModels()
   
...
}
public class ProfileFragment extends Fragment {
   
private UserViewModel userViewModel;
   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
       
super.onViewCreated(view, savedInstanceState);
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);
       
...
   
}
   
...
}

Пользовательские данные в UserViewModel предоставляются через LiveData , поэтому, чтобы решить, куда перемещаться, вам следует наблюдать за этими данными. При переходе к ProfileFragment приложение отображает приветственное сообщение, если данные пользователя присутствуют. Если данные пользователя имеют null , вы затем переходите к LoginFragment , поскольку пользователю необходимо пройти аутентификацию, прежде чем он увидит свой профиль. Определите решающую логику в ProfileFragment , как показано в следующем примере:

class ProfileFragment : Fragment() {
   
private val userViewModel: UserViewModel by activityViewModels()

   
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       
super.onViewCreated(view, savedInstanceState)
       
val navController = findNavController()
        userViewModel
.user.observe(viewLifecycleOwner, Observer { user ->
           
if (user != null) {
                showWelcomeMessage
()
           
} else {
                navController
.navigate(R.id.login_fragment)
           
}
       
})
   
}

   
private fun showWelcomeMessage() {
       
...
   
}
}
public class ProfileFragment extends Fragment {
   
private UserViewModel userViewModel;

   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
       
super.onViewCreated(view, savedInstanceState);
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);
       
final NavController navController = Navigation.findNavController(view);
        userViewModel
.user.observe(getViewLifecycleOwner(), (Observer<User>) user -> {
           
if (user != null) {
                showWelcomeMessage
();
           
} else {
                navController
.navigate(R.id.login_fragment);
           
}
       
});
   
}

   
private void showWelcomeMessage() {
       
...
   
}
}

Если пользовательские данные имеют null при достижении ProfileFragment , они перенаправляются в LoginFragment .

Вы можете использовать NavController.getPreviousBackStackEntry() для получения NavBackStackEntry для предыдущего пункта назначения, который инкапсулирует состояние, специфичное для NavController , для пункта назначения. LoginFragment использует SavedStateHandle предыдущего NavBackStackEntry , чтобы установить начальное значение, указывающее, успешно ли пользователь вошел в систему. Это состояние, которое мы хотели бы вернуть, если бы пользователь немедленно нажал кнопку возврата в систему. Установка этого состояния с помощью SavedStateHandle гарантирует, что состояние сохранится даже после смерти процесса.

class LoginFragment : Fragment() {
   
companion object {
        const
val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL"
   
}

   
private val userViewModel: UserViewModel by activityViewModels()
   
private lateinit var savedStateHandle: SavedStateHandle

   
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        savedStateHandle
= findNavController().previousBackStackEntry!!.savedStateHandle
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false)
   
}
}
public class LoginFragment extends Fragment {
   
public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL"

   
private UserViewModel userViewModel;
   
private SavedStateHandle savedStateHandle;

   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);

        savedStateHandle
= Navigation.findNavController(view)
               
.getPreviousBackStackEntry()
               
.getSavedStateHandle();
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false);
   
}
}

Как только пользователь вводит имя пользователя и пароль, они передаются в UserViewModel для аутентификации. Если аутентификация прошла успешно, UserViewModel сохраняет данные пользователя. Затем LoginFragment обновляет значение LOGIN_SUCCESSFUL в SavedStateHandle и извлекает себя из заднего стека.

class LoginFragment : Fragment() {
   
companion object {
        const
val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL"
   
}

   
private val userViewModel: UserViewModel by activityViewModels()
   
private lateinit var savedStateHandle: SavedStateHandle

   
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        savedStateHandle
= findNavController().previousBackStackEntry!!.savedStateHandle
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false)

       
val usernameEditText = view.findViewById(R.id.username_edit_text)
       
val passwordEditText = view.findViewById(R.id.password_edit_text)
       
val loginButton = view.findViewById(R.id.login_button)

        loginButton
.setOnClickListener {
           
val username = usernameEditText.text.toString()
           
val password = passwordEditText.text.toString()
            login
(username, password)
       
}
   
}

   
fun login(username: String, password: String) {
        userViewModel
.login(username, password).observe(viewLifecycleOwner, Observer { result ->
           
if (result.success) {
                savedStateHandle
.set(LOGIN_SUCCESSFUL, true)
                findNavController
().popBackStack()
           
} else {
                showErrorMessage
()
           
}
       
})
   
}

   
fun showErrorMessage() {
       
// Display a snackbar error message
   
}
}
public class LoginFragment extends Fragment {
   
public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL"

   
private UserViewModel userViewModel;
   
private SavedStateHandle savedStateHandle;

   
@Override
   
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        userViewModel
= new ViewModelProvider(requireActivity()).get(UserViewModel.class);

        savedStateHandle
= Navigation.findNavController(view)
               
.getPreviousBackStackEntry()
               
.getSavedStateHandle();
        savedStateHandle
.set(LOGIN_SUCCESSFUL, false);

       
EditText usernameEditText = view.findViewById(R.id.username_edit_text);
       
EditText passwordEditText = view.findViewById(R.id.password_edit_text);
       
Button loginButton = view.findViewById(R.id.login_button);

        loginButton
.setOnClickListener(v -> {
           
String username = usernameEditText.getText().toString();
           
String password = passwordEditText.getText().toString();
            login
(username, password);
       
});
   
}

   
private void login(String username, String password) {
        userViewModel
.login(username, password).observe(viewLifecycleOwner, (Observer<LoginResult>) result -> {
           
if (result.success) {
                savedStateHandle
.set(LOGIN_SUCCESSFUL, true);
               
NavHostFragment.findNavController(this).popBackStack();
           
} else {
                showErrorMessage
();
           
}
       
});
   
}

   
private void showErrorMessage() {
       
// Display a snackbar error message
   
}
}

Обратите внимание, что вся логика, относящаяся к аутентификации, хранится в UserViewModel . Это важно, поскольку ни LoginFragment , ни ProfileFragment не обязаны определять способ аутентификации пользователей. Инкапсуляция вашей логики в ViewModel упрощает ее не только совместное использование, но и тестирование. Если ваша логика навигации сложна, вам следует особенно проверить эту логику посредством тестирования. Дополнительную информацию о структурировании архитектуры вашего приложения на основе тестируемых компонентов см. в Руководстве по архитектуре приложения.

Вернувшись в ProfileFragment , значение LOGIN_SUCCESSFUL , хранящееся в SavedStateHandle можно наблюдать в методе onCreate() . Когда пользователь возвращается к ProfileFragment , будет проверено значение LOGIN_SUCCESSFUL . Если значение false , пользователь может быть перенаправлен обратно в MainFragment .

class ProfileFragment : Fragment() {
   
...

   
override fun onCreate(savedInstanceState: Bundle?) {
       
super.onCreate(savedInstanceState)

       
val navController = findNavController()

       
val currentBackStackEntry = navController.currentBackStackEntry!!
       
val savedStateHandle = currentBackStackEntry.savedStateHandle
        savedStateHandle
.getLiveData<Boolean>(LoginFragment.LOGIN_SUCCESSFUL)
               
.observe(currentBackStackEntry, Observer { success ->
                   
if (!success) {
                       
val startDestination = navController.graph.startDestination
                       
val navOptions = NavOptions.Builder()
                               
.setPopUpTo(startDestination, true)
                               
.build()
                        navController
.navigate(startDestination, null, navOptions)
                   
}
               
})
   
}

   
...
}
public class ProfileFragment extends Fragment {
   
...

   
@Override
   
public void onCreate(@Nullable Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);

       
NavController navController = NavHostFragment.findNavController(this);

       
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
       
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
        savedStateHandle
.getLiveData(LoginFragment.LOGIN_SUCCESSFUL)
               
.observe(navBackStackEntry, (Observer<Boolean>) success -> {
                   
if (!success) {
                       
int startDestination = navController.getGraph().getStartDestination();
                       
NavOptions navOptions = new NavOptions.Builder()
                               
.setPopUpTo(startDestination, true)
                               
.build();
                        navController
.navigate(startDestination, null, navOptions);
                   
}
               
});
   
}

   
...
}

Если пользователь успешно вошел в систему, ProfileFragment отображает приветственное сообщение.

Используемая здесь методика проверки результата позволяет различать два разных случая:

  • Начальный случай, когда пользователь не вошел в систему, и ему будет предложено войти в систему.
  • Пользователь не вошел в систему, поскольку он решил не входить в систему (результат false ).

Различая эти варианты использования, вы можете избежать повторного запроса пользователя на вход в систему. Бизнес-логика для обработки случаев сбоя остается за вами и может включать в себя отображение наложения, объясняющего, почему пользователю необходимо войти в систему, завершение всего действия или перенаправление пользователя в пункт назначения, не требующий входа в систему, как это было в случае с предыдущий пример кода.