when I am in the last activity and I press back, my app goes to the background but when I return my app restarts going back to the "login" screen, if I am in "Home" and I go back it goes to the background because my login activity finished, I understand this is part of how the Android system works. Having an app with an sdk that uses bluetooh with asynTask tasks, I would like to be able to force an activity not to close or cancel when it goes to the background, I am looking for alternative solutions such as making the app stay in "widget" mode or being able to launch some kind of "service" that forces my activity as such not to close or restart when it goes to the background, I'm quite new to all this, any suggestion on how to solve this problem will be welcome
Backing out of the last Activity on the stack treats the app as "closed", and there are expected behaviours associated with that:
The user's assumption in these complete dismissal cases is that they have permanently navigated away from the activity, and if they re-open the activity they expect the activity to start from a clean state. The underlying system behavior for these dismissal scenarios matches the user expectation - the activity instance will get destroyed and removed from memory, along with any state stored in it and any saved instance state record associated with the activity.
So while it is possible to restore the previous state (the example they give is a web browser opening at the same page it was on) the system and components like Navigation are built around this idea that state shouldn't be preserved. You'd have to put in effort to work around that.
Running a Service only keeps your app's process alive - it won't do anything for background Activities, which the system will destroy if it needs the memory. And besides, like it says above, if you back out of an Activity it's considered dismissed and its state is wiped, whether it's been kicked out of memory or not.
Honestly it sounds like you just want to avoid redisplaying the login screen if the user's already logged in? That shouldn't be anything to do with Activity state, you should be storing that data somewhere so you can check it when the app starts, so you can send them to the appropriate screen
There's a whole section about this exact scenario in the Navigation docs. They don't show their UserViewModel (which we're supposed to imagine handling logged-in state, and actually doing the user/password auth if necessary) but the idea is pretty simple - you have a component that exposes a value saying whether the user is logged in or not (which could be stored in e.g. SharedPreferences) and it can automatically navigate away from the login screen based on that.
Related
I recently wrote a demo app, which just needed to display some data temporarily --- I meant for the data to disappear once the app was properly destroyed by the user. Toward this, I read the page
The Activity Lifecycle , which seems to recommend overriding the Activity methods
onRestoreInstanceState() and onSaveInstanceState().
It worked great! The data was preserved through screen rotations, and sending the app to the background.
But then I would leave the app running and walk away, and when I looked at it again, the data was gone.
I spent hours trying to de-bug my app, and re-reading that page.
Finally, I read
Saving UI States. It refers to overriding these methods as "ViewModel" approach, and explicitly states that data saved this way does not survive system-initiated process death --- which explains my observation.
My main question is: what on earth is the practical application of this "ViewModel" persistence approach? What is the use-case for a persistence mechanism that randomly disposes of data when the user isn't looking?
(I guess this is an old API left over from the times when apps didn't run in the background. But I don't see that reflected in the documentation.)
A second question is, reading the first page, how on earth was I supposed to understand this unfortunate behavior? Did I miss something? (It is very long.)
what on earth is the practical application of this "ViewModel" persistence approach?
It is not a persistence approach. A ViewModel is a way of holding onto state across configuration changes. Using a SavedStateHandle with ViewModel — which maps to onSaveInstanceState() and onRestoreInstanceState() — is also useful for a fairly narrow use case:
User is in your app and does something that you don't want to save to disk or the server (e.g., the user didn't click "Save" yet)
User turns off their phone screen or switches to another app (e.g., via system HOME navigation or the overview screen)
Time passes
Android terminates your app process to free up system RAM for other apps
Within ~30 minutes of having left your app, the user returns to your app
At this point, Android wants to pretend that your app had been around all along, despite the fact that your process had been terminated. So, Android will not only start up a fresh process for you, but it will recreate the last activity the user had been on... and you get your saved instance state back as part of this.
However, this is not a persistence approach. For data you want to have survive long term, you need to save it to disk (SQLite, SharedPreferences, JSON file, etc.) or to some server. Notably, if the user leaves your app for an extended period (over ~30 minutes), Android will not attempt to restore the instance state, and your app will be started normally.
You need to use a SavedStateHandle with a ViewModel to get data persistence when the system terminates your app in the background. Otherwise it's more about sharing data between components, and surviving Activity destruction e.g. on screen rotation without having to do a lot of boilerplate handling.
Just like with onSaveInstanceState, this is purely about persisting data when the system kills your running app to recover memory, so that when the user switches to the "running" app again, it can be recreated and restored exactly as it was. It doesn't save any data when the app is intentionally stopped, e.g. calling finish(), the user backing out or swiping it away etc.
This stuff should always just work - if you were seeing your data "go missing" and the app wasn't crashing in the background, it's possible your save/restore logic wasn't working. A good way to test that is going to Developer Options on your device (if you don't know how to get that do a search, it depends on your device) and enable Don't keep Activities. That will destroy them as soon as they go to the background and it should help you test how that's handled. The fact you were handling rotations ok suggests it was a background crash though, but that depends on how you were handling configuration changes
I am using Singleton pattern. When application goes into background the OS kills the app to release memory. When user comes back to app my application starts from same Activity User left off but my singleton instance is not available because it was released too when OS released memory.
Suppose I am at Activity 5 and I have values int a = singleton.getInt() it will give me a null pointer exception. Other local views initialized are null too. But if my application restarts from splash screen then its all good because app flow is starting from scratch. Sometimes it starts from Splash, some times it goes to Activity 5 or whatever activity User left off (which will cause null pointer exception). I have used a lifecycle Observer to monitor application state in MainApplication but what exactly is the root of this issue. Any ideas?
Why does this happen?
When the system kills your application to free the memory it saves a few pieces of data about the current state of your Application into the internal OS storage. Later on, when the user opens your app once again, the system takes that saved data from the storage and tries to restore the state of the Application.
The reason for that behavior is to make the user experience smoother. By restoring the state - the user can quickly continue doing things he was doing before quitting your application.
Some of the data that is stored in the OS storage:
Activities back stack (back stack of the Task)
FragmentManagers back stack
Bundle that you saved in the onSaveInstanceState methods
Please note: ViewModels are not saved between application re-creation. Even though they are saved upon Activity recreation(configuration change, such as screen rotation). So data that you store inside of your ViewModels (if any) would be lost!
In order to support this OS feature and ensure a smooth user experience in your application, you must design your application to not depend on any Launcher Activity to instantiate your singletons and instead use some other architectural approach that would not require your Launcher Screen to launch every time the application restarts.
What can you do?
You could try one of the following:
Save all the user input data (if any) to the Bundle in onSaveInstanceState method.
Migrate from Splash Screen Activity or Launcher Activity in favor of the Reactive approach to data instantiation.
Move your Singleton instantiation to Application class. Note: This could significantly increase the launch time of your application.
That is by no means an exhaustive list of actions you could do. I bet you would have better ideas that are applicable to your own application.
If migrating away from Launcher Activity is not possible at the moment, I suggest adding to your Activity onCreate method a check that everything is instantiated and ready to use. If not - immediately navigate the user to your Launcher Activity.
Please note: It would be good UX to save back stack and navigate back to the current activity as soon as your logic on Launcher Activity is done working.
I face the following very annoying problem:
I'm using the Google Compatibility Library in order to future-proof my apps.
Now... I'm keeping track of my backstack:
1) Launch App.
2) User Interaction A -> Fragment gets added to UI/ Back Stack.
--- Backstack Size: 1 ---
3) User "backgrounds" the app.
4) User kills the App with a Taskkiller / or the app gets killed by the android system
5) Launch app again. Full Restart of the application (Application.onCreate()) presented to the User.
6) User Interaction A -> Fragment gets added to UI/ Back Stack.
--- Backstack Size: 2 ---
At this point I would want to have a backstack size of 1.
If the user presses back now, the app takes him back to some former state, which doesn't make sense anymore, because the app presented a fresh start.
Any Idea on How to do this??
Thx
Although your application has indeed been killed, there is not technically a 'full restart' when you try to launch it again. This is certainly not the way Android would want you to consider the situation.
When you background your app, Android will save the state of the fragments that have been added to the fragment manager (as well as the state of which activities are open, and their view state). When you relaunch your app after getting killed, Android will give you back all of this state and so you should be able to resume from exactly where you left off. This means you should consider that 'User Interaction A' has already happened and really you are at step 2) again. As you have noticed, the backstack is preserved; this is because the fragment transactions you have performed are also preserved.
The problem is of course, that we don't always write our apps in a way to match this behaviour. If your app presents a 'fresh start' after being killed whilst backgrounded, then arguably it is not really following the Android approach (assuming there is some user state worth saving).
I think you should try as best you can to resume as at step 2); it will likely be (marginally!) easier than trying to prevent Android from saving this state.
How can I have my Android app resume where it last was, i.e. the Activity in view, etc.? If I am showing an Activity and press the Home button and then launch my app again it returns to the startup activity. I'd like it to work like an iPhone app where it suspends in place and resumes back to where the user last was.
Thank you.
You may need to set android:alwaysRetainTaskState="true" for your root activity in the manifest. From the documentation:
Whether or not the state of the task
that the activity is in will always be
maintained by the system — "true" if
it will be, and "false" if the system
is allowed to reset the task to its
initial state in certain situations.
The default value is "false". This
attribute is meaningful only for the
root activity of a task; it's ignored
for all other activities.
Normally,
the system clears a task (removes all
activities from the stack above the
root activity) in certain situations
when the user re-selects that task
from the home screen. Typically, this
is done if the user hasn't visited the
task for a certain amount of time,
such as 30 minutes.
However, when this
attribute is "true", users will always
return to the task in its last state,
regardless of how they get there. This
is useful, for example, in an
application like the web browser where
there is a lot of state (such as
multiple open tabs) that users would
not like to lose.
Be sure to save any information you want in your onPause method and check that information in onResume. This will let the app reload any information that was communicated. Some widgets keep state but others don't.
Additionally when an an activity is killed it's onSaveInstanceState method is called. So anything that needs to be stored to persistent memory should be done there.
Although i'm working with Mono for Android i had a similar question.
After some time researching on the web you can setup an Activity to be created using the singleton pattern. This will cause your app to only create one instance and - if you go back to the activity - this will load the last state of that activity for you.
You can do this by configuring the activity with the
android:alwaysRetainTaskState="true"
and the
android:launchMode="singleTask" //(preffered, or "singleInstance")
properties.
Singletask is the best since singleInstance is more suitable for single-activity-based apps.
In my case setting the launchmode made this baby rock as i wanted but i don't know if this is a Mono for Android or Android issue at the root..
Be aware: you will need to create an initialize method or something alike if you'd like to create a clean/fresh/new instance of the activity at some point.
Often when I open my Android app from either my Home screen shortcut or from the apps menu it restarts the main activity.
How can I prevent this behavior and make it bring the existing instance to the foreground if there is one running?
I've searched quite a bit to no avail. It would seem as though most others are not having this problem?
Thanks!
You can never assume that Android retains the state of your app after you move away from it - because of a low memory situation, it may have removed it from memory.
Your best bet is to store the current state (including the current activity) in your SharedPreferences, and in your main activity (the one marked as LAUNCHER in your manifest), you check the shared preferences and go to the activity marked there. You can also use the SharedPreferences to store other information about the activity, i.e. you can use it to fill out text fields and other things with whatever values they had when the user left.