Here's my two scenarios.
1 -
User opens app for the first time ever from android home screen
User is presented with "first time" screen (backed by first time activity, lets call it A)
User hits back button
user is returned to android home screen
2 -
User opens app for second time
User is presented with the main list screen of the application (backed by a list activity, let's call it B)
User hits back button
User is returned to android home screen
I'm already aware of numerous ways to detect whether it's the first time opening the app or not.
The problem is in having the back button return to the home screen rather than a routing activity that decides which screen to forward to.
Currently My app has an activity to decide where to route (lets call it R) the problem is, My stack either looks like R -> A or R -> B
I want A or B to replace R on the stack when they open, and if the user hits back, then they go to the android home screen, not back to R.
Having a collaborator that sets the view for A and B is also not really workable as B extends androids concrete implementation of a list Activity to get most of its functionality.
Any ideas?
I want A or B to replace R on the
stack when they open, and if the user
hits back, then they go to the android
home screen, not back to R.
Call finish() in R after it calls startActivity() to trigger the opening of A or B.
Related
I am new to Android Programming.
I want to understand how Activity Stack is maintained for a particular Android Application and how does it changes based on user navigation.
For example, if there are multiple activities then how Activity Stack behaves when user clicks on Back Button or Home Button or launches a new Activity?
I was trying to find a suitable post where I can get all the information, but I did not get any. Can somebody please suggest me some links/posts where I can learn this?
Thanks!
Edited:
Links/Posts that I came across so far:
onSaveInstanceState is not saving my values ( onCreate input Bundle is always null )
Saving Android Activity state using Save Instance State
Android: Launch mode 'single instance'
Do you mean activities and the back stack?
Here is a link:
http://developer.android.com/guide/components/tasks-and-back-stack.html
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack), in the order in which each activity is opened.
The device Home screen is the starting place for most tasks. When the user touches an icon in the application launcher (or a shortcut on the Home screen), that application's task comes to the foreground. If no task exists for the application (the application has not been used recently), then a new task is created and the "main" activity for that application opens as the root activity in the stack.
When the current activity starts another, the new activity is pushed on the top of the stack and takes focus. The previous activity remains in the stack, but is stopped. When an activity stops, the system retains the current state of its user interface. When the user presses the Back button, the current activity is popped from the top of the stack (the activity is destroyed) and the previous activity resumes (the previous state of its UI is restored). Activities in the stack are never rearranged, only pushed and popped from the stack—pushed onto the stack when started by the current activity and popped off when the user leaves it using the Back button. As such, the back stack operates as a "last in, first out" object structure. Figure 1 visualizes this behavior with a timeline showing the progress between activities along with the current back stack at each point in time.
I need this behavior in my app, "user must have to login in each time he tries to launch it(touching the app icon directly, or by task manager or via recent apps)"
So the activities are in this order (think the App name is Foo app)
S - Splash Screen
L - Loading Screen
Lo - Login Screen
M - Main Menu (Has list of tasks)
T - Task Screen
"User starts the app and proceeds as in the following order :
S -> L -> Lo (logs in) -> M (selects one of the Tasks) -> Task Screen
so while he is in the Task Screen, he presses the Home button and the App goes to the background and he uses another app. and lunches the Foo app again. So in this if he pressed the Back button, it shows the previously being used Task Screen??? and if pressed Back button again, it goes to the Main screen again, and so forth...???
This should not happen, simply what I want is, when the App comes to foreground, user needs to login and never be able to go to back to any of the screens.
Note : all the screens have extended a BaseActivity class which has extended the Activity class. and in there I have used onResume(),onPause() method and another custom method to find out when the extended child class goes to pause,and resume when the app comes from the background!!! And in some Screen I have had to use Fragments too...!
Thanks in advance for your time and help!
Add tag android:clearTaskOnLaunch="true" to root (main) activity tag in manifest. Then user will always return to this activity.
To jump to Login from any other activity, use:
public static void logoff(Context c){
Intent logoff = new Intent(c,LoginActivity.class);
logoff.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
c.startActivity(logoff);
}
I am not going to argue why your question describes a not so pleasant user-experience, but what would you say about below solution?
Replace Lo, M and T as fragments. So after L you would have an activity (PostL) where you show first Lo fragment, then on user action you would add on top of Lo an M fragment by get(Supported)FragmentManager.beginTransation().add(id, fragment).commit(), then moving on you would replace M by T: get(Supported)FragmentManager.beginTransation().replace(id, fragment).commit(). - or you can add T on top of M, it's your choice as it might suite better.
In this PostL activity, in onPause() method you would call only get(Supported)FragmentManager.popBackStack() if the fragment backstack is consisted of more than one fragment - the bottom fragment is Lo.
In this way would be covered in both cases when the user taps on Home screen and re-enters the app (no matter from where), or any other app goes in the foreground. Also, if you consider correctly the backstack size, if the user taps back then he will always open the last fragment (that could be M or Lo).
I hope it makes sense ...
I read about tasks and back stack (http://developer.android.com/guide/components/tasks-and-back-stack.html) but still having few confusions . I was just trying different things and stcuk on one case . So lets take a example :
We have two apps A1 and A2. A1 has one activity say A1_first(also the main activity) and A2 has two activities A2_first(main activity) and A2_second. A2_second is a singleTask activity. A1_frist calls A2_second on button press and A2_first also calls A2_second on button press.
If I launch A2 I can see the A2_first screen and after button press I go to A2_second(as expected) but suppose I first launch A1 and after button press A2_second , now press home button and again A2 icon from launcher , I reached to A2_second but I expected to reach A2_first.
I didn't understand what I am missing . Can someone explain
pressing A2
A1_first ----- > A2_second ----> home -----------------> A2_second (Why not A2_first ?? A2_first is main activity for A2).
If you launch an application from the HOME screen, it doesn't necessarily take you to the first activity of that application. If the application is already running, it just returns you to where you left off in the application. This is what you are seeing. In addition, you've made things more complicated by using "singleTask" launch mode. In general, you shouldn't be using "singleTask" or "singleInstance" launch modes. These are very special launch modes used mostly for creating HOME-screen replacements. In any case, if you need to use one of the special launch modes you need to make sure that you have a different application icon for the activities that use these launch modes. If you were had different application icons for A2_first and A2_second, then it would be more obvious what is going on.
If I understand you correctly you are not exiting the application, but just pressing the home button. If the application state is not changed it will come up back from cache with the same activity opened as you left it before pressing home.
Try How to finish() an Activity when Home button pressed for more details on how to finish app on Home button press
If you start (successfully) activity B from the activity A, and then press "back" , you will be back in the activity A. Independently on which applications do these activities belong to.
There is no standard "home" command in Android for returning, sorry. On my phone, for example, "home" will return to the start screen, with all activities put to the background. Obviously, you don't mean it.
I this is not enough, put here the code, containing activities calling and processing of returns. It is hard to say something not knowing, what exactly your call buttons and return processes do.
And before understanding tasks and backstacks, I would advise to understand activities starting/returning first.
When you press the home button, A2_second just goes into the background. It does not end (finish). So when you click A2 icon, the system will look for the last accessed activity from A2 (if any). Since A2_second is available and is in a background state - The system will simply call it back to the foreground.
This is how android establishes multi-tasking. The whole app (all the activities) are never loaded into memory at once. Instead, an application is broken into chunks of functionality (activities) that can be loaded as and when needed. So when you call an activity from another app (calling A2_second from A1_first), and then press home, This activity (A2_second) will go to the background. When you click the A2 icon, since the system knows that A2_second is in the background, It is brought to the foreground since the user has accessed this last and is probably the part that he/she is looking for.
If you press "back" however, A2_second will be finished. After this, If you click on the A2 icon, then A2_first will be launched.
This way, different parts (activities) from various apps can co-exist in memory and provide a seamless experience to the user while keeping the system fast and snappy.
I'm ware of androids life cycle and that it's not needed to add a "exit" button in the application.
But still, this back button stuff isn't really working out well.
You maybe know this issue from the default SMS-App that android comes with: you open it when you get a new message and exit it using the menu button or something else.
After like 20 times doing this, you then decide to exit the app using the back button, what happens? you have to go back though 20 views. every time you press back you return to the "all messages (by sender)" listview and when you press back there again you return to the message opend 20-1 (message 19). again you press back and return to the listview and again you press back and end up at message 18. till, after40 times pressing back, you finally exit the messanger app.
same happens when for example you got a action bar with a "home" icon which opens the main screen of your app. the user picks a action and the new activity starts. than the user clicks the home button and returns to the main screen. when pressing the back button - no matter if you call finish() in the onButtonBack listener or not, you the user would expect the app to exit, but in fact the app returns to the previous activity which is wrong.
such cycleing may happen for various reasons, thats why - even thought i'm aware of the supposed to be lifecycle of android - i wan't to EXIT (& destroy) the app when pressing back within a defined activity.
calling finish() dosn't help. if there's a previous activity it will re-open it. calling system.exit(0) isn't nice to do.
so: whats the right way to prevent such back-press-cycles and/or exit a application (WITH destruction)?
for better illustration of what i want to achieve: consider A, B, C being activities. a arrow (-->) illustrations a new intent call from the activity leftside of the arrow, rightside of the arrow represents the activity that is called. ex.: A --> B means activity A starts activity B. now here's what i want:
1) A --> B --> C pressBack:--> B pressBack:-->A pressBack:--> Exit
2) A --> B pressBack: --> A pressBack: --> Exit
3) A --> B --> A pressBack:--> Exit
as you see, back works as always, BUT when in activity A it exits the application.
the behaviour i got now is 1) and 2) as above but
3) A --> B --> A pressBack:--> A pressBack: --> Exit
keep in mind, i've already overwritten the onBackPressed listener of activity A with a finish() call. even calling system.exit(0) dosnt work. however, even if it would: its not what i want, i want the REAL way to do it android style - i cant imagine system.exit(0) is best practise.
Well this is the default behavior.
If you have another approach, just implement it.
One approach to deal with this is to use the android:launchMode="singleInstance" for activities that can be launched in a singleton manner (only one activity can exist)
For example, if the SMS page in the SMS app was a singleTop, it would have needed only one back press to remove all the SMS pages. It is a matter of choice
Another more aggressive way would be to finish Activities when you start another activity. Of course, such decision would risk making the app less friendly (android users are not accustomed to this behavior). Nevertheless, if this is used only where it may be considered acceptable then it might be acceptable.
A very acceptable place to do this would be a login screen: Once login is successfull, you start another activity (probably designed for logged in users) and finish the login activity.
Enjoy Finally, in my personal opinion, you can add an Exit button. Users will find it nice.
Check my post: Adding an Exit button to Android Application
In my situation, there is one case in which I need to make sure the activity only runs one at a time.
I found if I set the LauchMode of the activity, I can reach the single instance aim, but it won't update the view of the activity.
This activity is launched by startActivityForResult, and we send the URI with the intent to the activity.
Let's discuss with this certain case:
gallery - lauch this activity with imageA.
camera - lauch this activity with imageB.
My request is not to destroy the old activity, but the activity that just received the new intent infomation should refresh the view.
I found a new method, onNewIntent.
This method can refresh the intent before resume. I will try it.
You can have an Activity with a manifest attribute of singleInstance.
As soon as the activity is relaunched , onResume gets called. You can update the view with the new image and invalidate the older View.
<activity ..
android:launchMode= "singleInstance" />
In your manifest, use android:launchMode="singleTask" instead of android:launchMode="singleInstance". Using singleInstance only returns to the existing instance if it is at the top of the stack. singleTask, on the other hand, return to the existing instance of the activity, as it is always at the root of the task.
Then, when your instance is launched, override onNewIntent to update your UI according to the new intent.
Refer to the Andorid documentation for details.
Be careful when using android:launchMode="singleInstance" in multiple activities application.
Here is my test on Pixel 4XL (Android 10)
Example, we have 3 activities (without declare taskAffinity)
A (entry activity)
B (start from A)
C (singleInstance, start from B)
If we start A->B->C. Now A,B in a task and C in a different task.
From C, press back -> will see B, press back -> will see A, press back -> app close. Everything working efine.
However, if we start A->B->C then press Home -> app go to to background.
Now there is 2 situations
User open app again from Recent app.
User will see C screen. Press back -> app will close (don't go to B)
User open app again by click on app icon
User will see B screen. Press back -> will see A, press back -> app will close (don't go to C)
The behaviour after open application is different between both case. But in both case, the application is always available in Recent app and you never able to remove application from Recent app (after remove it will appear again). Only able to remove it by Force stop in Setting.
If you run adb shell dumpsys activity activities, you will see that activities still exist in Stack, but you can not go back to it.
Another case
If we start A->B->C->A. Now A,B,A in a task and C in a different task (A is current visible to user).
From A, press back -> will see B (not C), press back-> will see A, press back -> will see C, press back -> app will close. (at least here we can go back to C)
Now if, we start A->B->C->A then press Home. In both case, user open app again by Recent or click on app icon, user will see A screen. Press back -> will see B, press back -> will see A, press back -> app will close (never see C screen)
Another note
The above example, although application run on 2 tasks but in Recent apps, it only show 1 task.
If you set the taskAffinity, in the Recent apps, you will see 2 tasks. In that case, you can choose the task which contain activity that you want to display but when you press back, it just go back to the activity in same task or close app (not able to go back to activity in another task).
Also, if you click on app icon to open app again, we have the same behavior like above demo.
I think singleTask and singleTop is less complex than singleInstance, so if it is enough for solve your problem, consider to use them instead of singleInstance