The two screens relevant to my issue amount to MainFragment (a master view) and ProfileFragment (a detail view). The launch path from login -> main -> profile works fine. The return path isn't working as well. I get stranded at main when navigating backward with via gesture or device button.
On the initial "back" from the profile, I'm returned to main and see main's onViewCreated, onResume lifecycles. I expect back from main would return to login or even exit the app, but instead it reloads main a dozen times before crashing.
For any subsequent "back" from main, the logs reveal main's onAttach, onCreate, onViewCreated, onResume lifecycles recurring each time. This seems suspicious although it's a pretty vanilla navigation graph, a single Activity hosting four basic Fragments. They're all navigated with basic actions using their generated, type-safe Directions classes. I haven't overridden onBackPressed in any of these classes.
Combinations of app:popUpTo="#+id/loginFragment" and/or app:popUpToInclusive="true" haven't helped.
Do I need to implement back or "up" in order to work as expected? Any thoughts are appreciated.
On closer inspection, it's working the way it's coded. Of course it is!
The preceding LoginFragment checks a SharedPreference value and navigates to main. I confirmed that happens so quickly I thought I never returned to login screen, when in fact I did.
I'll investigate removing login screen from the stack after authentication.
Related
Today I've started playing with Android's Navigation Component to implement authentication flow in my app. The idea is very simple: a user launches the app and it displays one piece of UI if she's authenticated and another if she's not.
What I've done is in my HomeFragment's onViewCreated method I check if a user is authenticated and if she's not I call the NavController's navigate() method by passing it the id of an action that will navigate to the AuthenticationFragment. Everything works fine until a user clicks the back button while in AuthenticationFragment, because then I get this error. I still have no idea what's the actual reason for that error, but the idea of navigation happening too quickly seems similar to my case: the destinations first switch from AuthenticationFragment to HomeFragment and then immediately from HomeFragment to AuthenticationFragment again, because HomeFragment finds out again that a user is not authenticated.
Despite of the error I'm getting, this approach still seems wrong to me. The user shouldn't be able to go back to the HomeFragment (and see the screen flicker when fragments are immediately switching back and forth) before she authenticates. I've also looked at this Android's official guide to implementing an authentication flow, but it too seems wrong to me, because a redundant third piece of UI is involved there (the MainFragment). I could replace it with a splash screen in my case, but it would still stay in the back stack and the user would be able to go back to splash screen, which is obviously nonsense.
So what would be the correct way to implement an authentication flow using the Android's new Navigation Component? The functionality I want is: if a user is not authenticated, then she's redirected to an authentication UI and if she presses the back button from there she should exit the application.
UPDATE: I know I could just listen to the back press event and close the application from there, but I still hope there's some elegant solution using Android's Navigation Component.
In your login fragment, you need to declare "Pop up-to" action from home fragment.
This pops all non-matching destinations from the back stack until
this destination is found.
"popUpToInclusive = true" pops the given destination from backstack
<fragment
android:id="#+id/loginFragment"
android:name="com.example.navigationsample.fragments.Login"
android:label="Login_Fragment"
tools:layout="#layout/layout_login">
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#id/loginFragment"
app:popUpToInclusive="true"/>
</fragment>
I have ActivityA which contains FragmentF.
ActivityA contains also NavigationDrawer infrastructure.
When I start the app - all is fine.
There are called (among others):
ActivityA.OnStart
FragmentF.OnStart
ActivityA.OnResume
FragmentF.OnResume
.. and content is shown.
But when I press home (to minimize app & show home screen) - here comes the problem:
ActivityA.OnStop is called
FragmentF.OnStop is NOT called
Interesting facts:
- if I switch items in NavigationDrawer then FragmentF.OnStop is called and content of another fragment is loaded in ActivityA
- in all cases (minimize the app & switch content in NaviDrawer) FragmentF.OnPause is always called
As a workaround I put code (expected to work in OnStop) to OnPause but wondering:
- why FragmentF.OnStop is not called
- how to make it called
I'm using Xamarin (thus CamelCase namie convention :) - but I don't suspect this platform for bug, it seems like Android native behavior.
First of all, I do not know what kind of specific layout in your project or use other plugins. I test it in my demo(Activity contains Fragment).
github.com/851265601/FragmentDemo
When i click the home button, Onstop of Fragment was called like screenshot in this demo. This issue may related other reasons, do you use Util.log to generate it and capture the log in logcat. Note: Don't put time-consuming operations in OnPause
The home screen of my app needs to have different behavior when the app is being launched from outside the app versus when a back navigation occurs. For example, imagine an Android Twitter client which on launch tries to get your updated feed but when you click on an item to hit its detail page and hit back, the screen doesn't reload new feeds but is instead where you left from.
So far I've tried looking at the calling package property but this hasn't helped since it seems to be null both when app is launched by the user for the first time or when I hit back from a detail page.
When user launches it first time, onCreate() then onresume() will be called for sure. When navigating back and getting your activity from backstack, just onResume will be called.
Also if you activity has singleTop set, then you can look into onNewIntent() which will be called when you navigate back to your activity from other screens.
So, the solution I can suggest you is to set singleTop for your activity. Then while navigating back using onNewIntent(). For 1st time launch onCreate() will be called.
Sorry if I could not understand your question well..
The official Dev Guide of Tasks and Back Stack says, activities can be instantiated multiple times, and Home Activity is taken as an example
So I tried it out as the graph illustrates:
Launch Activity 2
Press Home button
Launch Activity 1
Press Back button (so I return to Home screen)
Press Back button again
But I did not go back to Activity 1. Thus, it seems that Home Activity has not been instantiated multiple times. Is it so? If so, how is it kept in a Back Stack?
EDIT: Sorry, I should've clarified earlier that I didn't write any codes to test it. All I've done is just launching applications on favorites tray. I'd better go to read the source code and search for the behavior of Home Activity.
Anyway, I don't think Home Activity is a good example here to illustrate multiple instances.
Your issue might be that you might have called finish() in your Activity2. Or, the OS clears up the 2nd Activity before you return back to it. The behavior you are trying to attain on your own has no guarantees. You can't force an Activity to keep running so that you can return back to it.
I'm experiencing kind of strange behavior of my application after hard Home button is pressed.
When you press Home, everything is OK - my app goes to the background, showing Home screen. But if you try to choose my app in the main menu or in the list of last tasks it behaves like it was not started before and does not show the last activity you were on - it just starts from scratch, namely, shows the splash screen and starts next corresponding activities. Moreover, old activities of this app remain on the activities stack, and previous instance of the app is not terminated - so if you press Back for a few times you'll just run into those activities which were undoubtedly started during the previous session of work with my app. Splash screen activity is filtered by "android.intent.action.MAIN" filter and "android.intent.category.LAUNCHER" category.
The strange thing is that all of that happens despite the fact that I do not intercept any Back key hits, or override any onPause or onResume methods. What's happening contradicts with my understanding of Android app lifecycle - I was sure that when you hit Home an app just goes to the background, and when you choose it in the menu later - it just unwinds and does not start anew. (Of course, unless stuff like that is stated in the app manifest or corresponding methods are overridden or something else).
I also checked it for some other lifecycle events - such as changing orientation or flipping hard keyboard out - and none of those led to such strange results. It appears that the problem occurs when you try to start the app from main menu or menu of last applications.
I hope you will be able to help me. Any advice on what to pay attention to or where to search for solution would be really great.
Regards, Alex
You need to set android:launchMode="singleTask" in your LAUNCHER activity in your manifest file.
For more info on the launchMode attribute see here
Note that:
The default mode is "standard".
and:
Every
time there's new intent for a
"standard" activity, a new instance of
the class is created to respond to
that intent.