I have a problem understanding the difference between backButton issued as the Hardware button, and backButton issued as a super.onBackPressed() from the Activity (i.e. from menu android.R.id.home).
In the case of Hardware backButton the data in my Activity are initiated immediately and in the exact state which Activity hold before leaving.
In the case of on super.onBackPressed() it seems like the former Activity gets finished, since the data needs to be initiated from the scratch.
Tried studying the lifecycle but found nothing interesting for this usecase.
How can I mimic behavior of the Hardware Button, which would allow me to retain state of the Activity?
Can prepare some demo code, but I hope I provided good enough high level description.
Related
I am maintaining a simple Android kotlin app that only has one MainActivity.
The app has a lot of images. We want to reload the data and images when user navigate away and then return to our app, this works so far:
override fun onResume() {
super.onResume()
tryToLoadDataAgainAndReloadAlotOfImageViews()
}
However this produces one unpleasant side effect, for example:
User launch the app
onResume fired, we load data and refresh all images (corrrect)
App shows permission dialog to grant GPS locations
User click accept
permission dialog dismisses
onResume fired again, all data reloaded and images reloaded again.
This will looks like a glitch as all images are reloaded twice in a short period of time.
I understand that I may set booleans here and there before showing permission dialogs to resolve this, but it feels like a hack and possibly buggy code.
Is there any elegant way to resolve this? For example, detecting onResume is coming from another app, or coming from internal permissions dialogs?
We want to reload the data and images when user navigate away and then return to our app
What do you mean by this exactly? Are you talking about the app disappearing from the screen? (e.g. the user switches to another app, or hits the home button, or turns off the screen.) If so, do your refreshing in onStart instead of onResume, then it'll only fire when the app becomes visible again.
But if you want to handle things like multi-window mode, where your app is visible but might not have focus, then you'll need to look into detecting that when your app hits onPause, and maybe storing a flag onResume can check.
Basically Android's Activity and Fragment lifecycles have a couple of paired callbacks, onPause/onResume and onStop/onStart.
When your app is no longer in the foreground, but still visible, it moves to the PAUSED state. If it remains visible, but then moves to the foreground, it moves from PAUSED to RESUMED by calling onResume. This is what's happening with your dialog - because it's displayed over your Activity, the Activity is paused until the dialog is dismissed, at which point it calls onResume. So what's happening is by design!
But if your app is becoming invisible (i.e. it's not on the screen anymore) then it first moves to the PAUSED state (if it's not already), and then it moves to STOPPED and you get the onStop callback. When it returns to the foreground, onStart will be called, followed by onResume. (This is also what happens when the Activity/Fragment is (re)created of course!)
So you need to handle things in the appropriate lifecycle callback. It sounds like your needs are better met by having this stuff handled in onStart, since that only gets called when the Activity/Fragment appears. onResume can be called more often, since it's possible for the component to enter the PAUSED state (onResume is called when it's "unpaused") without moving to the STOPPED state - like with dialogs.
Like I said, if you want to handle multi-window mode that's more complicated, and the lifecycle behaviour depends on API level, so you'll have to look into that!
You can use the isFinishing method to check if the activity is in the process of finishing before calling tryToLoadDataAgainAndReloadAlotOfImageViews() in onResume()
try using Glide.
dependencies :
//Glide
implementation("com.github.bumptech.glide:glide:4.12.0")
kapt("com.github.bumptech.glide:compiler:4.12.0")
As per the official documentation
Glide is a fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.
When a user enters an activity on my app I want to perform some logic during onStart and possibly launch a second activity before letting the user see the first activity (think of this as a pin protected activity)
I have a small issue where the contents of the first activity are shown for a second before the second activity is started. This happens when the user uses the home button to get out and in to the app. Is there a way to prevent the first activity to be visible at all before performing the logic validation?
This is more of a "design" solution to your problem and not clear if it'll work for you. I had something similar in the app I'm working on. What I did instead, was to create an interstitial Activity that resembled the same starting state (i.e. not yet completely loaded) of the Activity (Pin-protected Activity in your case) that is about to be started. Once I'd made my appropriate decision about whether or not I could go on, I just navigated to that initial Activity. In your case, I could see you making the decision on this interstitial Activity, and then navigating to the Pin-protected Activity or to the other one if conditions were not met properly.
The only downside to this approach is that the app does a quick flash with the additional Activity, but I think the increased separation of logic is worth it.
I have an app in which I programmatically create an EditText view. I assign an ID to that view using setId()
myEditText.setId(100);
so that Android automatically saves that object's state when pausing/stopping the app (as I was advised to do here). It works in these cases:
(1) When I leave the app using the "Home" button: if I then come back to the app, the object's state (displayed text) is restored, as expected.
(2) On a screen orientation change (which involves Android automatically destroying the activity and restoring it through a Bundle). The object state is also kept.
However, it doesn't work in this case:
(3) When I leave the app using the "Back" button: if I then come back to the app, the EditText object is empty.
Any explanation as to why this happens? Does Android really distinguish between leaving the app with "Home" and with "Back"? According to the documentation, the object's state should be automatically preserved, through a Bundle, even when the activity is destroyed. And that clearly happens in case (2). But not in case (3)!
If this is normal behaviour, how could I have the app's state automatically saved and restored when the user presses "Back"? I know I could use the SharedPreferences for that, but I'd rather have Android do that automatically, just as it does in cases (1) and (2).
This happens at least in Android 4.0 and 4.2 (I haven't tested others).
You really should study activity life cycles as there are many many ways to solve the problem. Since your activity is typically pulled off of the stack and destroyed when you navigate back one quick but not necessarily the best way is to make sure your activity flagged as singleTop or singleInstance in the manifest that way it is not pulled off of the stack and recreated when you navigate back and forth. You could also use the singleton Application class. Or pass the text back and forth as params. Or use a database. Or use MVC or some other programming paradigm that will allow your views to be destroyed and recreated with out the data populating them going with it. Lots of "or's". Study activity life cycles and then look at how you have your application architecture setup already and choose the method that will work best for you.
http://developer.android.com/training/basics/activity-lifecycle/index.html
http://developer.android.com/guide/components/tasks-and-back-stack.html
I think I found the explanation. I only needed to read the doc more carefully (thanks to #lentz for one of the links); see here and here:
When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed.
If the user presses the Back button, the current activity is popped from the stack and destroyed. The previous activity in the stack is resumed. When an activity is destroyed, the system does not retain the activity's state.
The above explains behaviour (3) in my question.
However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actual Activity instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the "instance state" and is a collection of key-value pairs stored in a Bundle object.
The above probably explains behaviour (1) and (2).
What I don't see is why the user pressing Back should be interpreted as "the activity is no longer needed" ("its state needs not be preserved"). But that's a different matter.
I'm making a simple e-book reader app, and an activity can be called by many cases.
I'd like to distinguish callee activity to know its origin action in case of
From my another activity: this can be easily solved by
StartActivityForResult from calling activity.
Called by back button click from other package app after share action ("whoops, I missed to click share button, and then back.").
Switched by user's multitasking choice.
Called by user click at the start screen: this might be known by MAIN entry point at the android manifest.
How to know above cases?
I have no idea why you would need to do this but...
1.From my another activity: this can be easily solved by StartActivityForResult from calling activity.
Yes, as long as the calling Activity is your own as you can't guarantee any 3rd-party code will use startActivityForResult(...). You can, however, use getCallingPackage() and getCallingActivity() in other cases.
2.Called by back button click from other package app after share action ("whoops, I missed to click share button, and then back.").
When the user presses the BACK button your Activity isn't being "called" - it's simply being resumed or re-started. The original calling app/Activity/method will still hold true - there is no way to ascertain that this has happened as the normal Activity life-cycle methods (onStart() and onResume()) are always called even when an Activity is first created.
3.Switched by user's multitasking choice.
If you mean using the "Recent" apps view, the same applies for my answer to 2. above.
4.Called by user click at the start screen: this might be known by MAIN entry point at the android manifest.
In this case onCreate() will be called although if your Activity is simply stopped for whatever reason, it may simply be restarted depending on the launch mode you use.
In short, I can't see you being able to gather much in the way of any accurate information as to how your Activity becomes active.
I am not too sure about the actual way for the above question as I am too a new guy in android.
But to the best of my knowledge... called by back button and switched by user's multitasking leads the activity to enter pause state.
So you can access it from "onPause" method in your activity.
I'm developing an Activity that does some of its own state management. I'm trying to differentiate the following onResume cases:
New launch
task switch (home button long-click)
resume after other activity in the same application
wake-up after sleep
orientation change
Is there something in the Activity's intent, or elsewhere, that can help me differentiate these?
For the curious and some context... I'd like to preserve my internal history stack on 4 & 5. On cases 2 & 3, I would preserve the same current page, but erase the history (allow the normal back button functionality to take over at that point). Case 1 would initialize to the activity's internal start page (and can be detected easily enough with some help from onCreate).
Is there something in the Activity's intent, or elsewhere, that can help me differentiate these?
Item #4 has nothing to do with onResume(), AFAIK.
Item #5 would be better handled via android:configChanges and onConfigurationChange() though you could "detect" it by returning something from onRetainNonConfigurationInstance() and seeing if it is there in onResume() via getLastNonConfigurationInstance().
The others aren't just three cases, but probably twice that, once you start taking into account things like "being kicked out of memory to free up RAM" as a possibility.
Off the cuff, it feels like you made some unfortunate architectural decisions ("internal history stack...erase the history...allow the normal back button functionality to take over at that point"). Android is designed around lots of cheap activities, and you appear to be violating that precept. You are welcome to do so, but bear in mind that Android support for your chosen pattern may be limited.