This question has been answered numerous times it seems, but none of the answers solve the problem for me, so I thought I'd ask a new one.
My scenario is as follows:
I have added a navigation drawer to my app that holds list items for each section of the app. Tapping one of these launches the appropriate activity for that app section.
The way I wanted to do this was to have a main activity which had the drawer layout and then everything else would be fragments, but the app has a lot of activities already and the customer isn't willing to pay for the extra time that would be needed to convert these to fragments and get it all working. Therefore I'm keeping it as it is and have the nav drawer on each activity.
When the user clicks an item in the nav drawer, that section's activity is launched. If the user presses the back button on any of the activities that the drawer brings them to, I want the app to close (and go back to the Android app menu or home screen or whatever).
My problem is that pressing back will just pop the activity that the user was previously on when they clicked an item from the drawer. For example:
User is in Activity A.
User opens drawer and clicks list item.
Activity B is opened.
User presses the back button.
Activity A is shown.
I'm looking for a way to remove all previous activities from the stack, so that when a nav item is clicked, everything already in the stack is removed so that pressing back on the new activity will end the app because there won't be anything else to show.
The closest I've got is using the FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TASK flags on the intent that is launched on the nav list item click.
sectionIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
parentActivity.startActivity(sectionIntent);
(where sectionIntent is an Intent for the new activity and parentActivity is the activity hosting the drawer).
This actually works, but an empty (black) screen is displayed for half a second or so between the old activity closing and the new one showing.
I've also considered using LocalBroadcastManager to tell all activities to close when a "top level" (as such) activity is exited, but to be honest that approach seems like total overkill to me and surely a simpler option must exist?!
Does anyone know a way either to prevent this delay (like for the previous activities to be removed AFTER the new one appears) or another way to remove the activities in the stack?
Thanks in advance, fellow devs :)
-- SOLUTION --
Thanks to one of #Zielony 's suggestions, I managed to get this working.
I made a base top level activity and made it send a local broadcast to exit the app.
public class AppSectionHomeActivity extends BaseActivity {
#Override
public void onBackPressed() {
sendFinishActivityBroadcast();
super.onBackPressed();
}
#Override
public void finish() {
sendFinishActivityBroadcast();
super.finish();
}
private void sendFinishActivityBroadcast() {
// Send a local broadcast to all other activities to tell them to close.
Log.d("AppSectionHomeActivity.sendFinishActivityBroadcast", "Sending broadcast");
Intent intent = new Intent();
intent.setAction(Constants.EXIT_APP);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
I then made a base activity for all other activities (the ones that weren't top level ones) so make them listen for the broadcast and then call finish() on themselves if they received it.
public class BaseActivity extends Activity {
// A broadcast receiver so that we can listen out for feed updated events.
private BroadcastReceiver exitAppReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
unregisterReceiver();
// When the activity receives the broadcast to finish up, then do so.
finish();
}
};
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(exitAppReceiver, new IntentFilter(Constants.EXIT_APP));
}
#Override
protected void onDestroy() {
unregisterReceiver();
super.onDestroy();
}
private void unregisterReceiver() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(exitAppReceiver);
}
}
This seems to work!
You have several choices:
use Broadcast to tell the stack to close itself and then add new Activity. It's quite good solution, but seems like you tried something similiar. Also, this shows best that you shouldn't attempt to close multiple activities. I don't know the thought process behind that.
use Process.kill() to kill your application when exiting most recent top-level Activity. It would look like exiting the app without the rest of the stack. It's not that bad either - Android will close unused Activities anyway, so it's not that resource demanding.
use Fragment stacks. I know your client wants you to do it quick and cheap, but fragment stacks are really good for this.
use Tasks to group Activities and close groups at once. Seems like it's not working for you and I don't really know why. This is the pre-fragment solution for your case.
Have you considered using fragments instead of activities? This allows a lot more flexibility when things will be shown/hidden/available etc.
Are you using a new nav drawer for each activity?
You can try like this. such as
sectionIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
parentActivity.startActivity(sectionIntent);
Best of luck!
In Manifest file, which ever activity you don't want in stack, in that activity make this changes
android:noHistory="true"
I hope this works for you
Related
I have 2 activities (audio chat activity & message chat activity). Currently both activities working absolutely fine but now I want to both activity should work simultaneously like a Whatsapp messenger.
In audio chat activity there is a button to go into message chat activity but once message chat activity starts the audio chat activity stop working.
Is there any way where I can switch between these two activities like a Whatsapp messenger.
You should change approach. When you start a new Activity B, Activity A is paused, so it can't work. For your purpose you should use a single Activity and show/hide elements (e.g.: messagebox and audiobox)
There is no way to guarantee that an activity that sits below another activity will never get killed. Android can do this to reclaim memory when needed. It's the way Android was designed to facilitate devices with limited resources.
Instead, if you want to let the user seamlessly switch between text and audio chat, you will need to maintain the UI between those two components in the same activity. The easiest way is to simply have two different sets of views in the view hierarchy for the activity and switch between them by changing their visibility.
I hope this will helpful to developer,may be fresher or senior,I have ActivityA and B,this is my ActivityB class
#Override
public void onBackPressed() {
super.onBackPressed();
Intent intnt=new Intent(getApplicationContext(),ActivityA.class);
startActivity(intnt);
}
please use this step in ActivityB to move back your AcitivityA without killed
And solution is
#Override
public void onBackPressed() {
super.onBackPressed();
}
//I hide these below intent(2lines) in ActivityB,So it works thanks by Daemon....
I have an app with some normal behaviour but I still dont know what I am doing wrong.
Activity A calls --> Activity B
I pass strings/ints from one activity to the other (Intent.putExtra())
I had declared my activities as single instances and declared who is parent of who in my manifest, but when I open other app and then go back to mine, the activity I was in is the only still alive.
If I remove the'singleInstance', then my navigation works but the ones that receive strings from the intent (previous activity) crashes.
I want to be able to:
When I am in my app, navigate up/back through my activities.
If I leave my app and come back, still be able to navigate up/back in my app.
Considering I pass values between activities with Intent.putExtra("key", "value")
I think is all related with the Back/Up Navigation and the android:launchMode=["multiple" | "singleTop" | "singleTask" | "singleInstance"] but I can't find the perfect solution.
Update:
A --> B --> C
A->B: A putExtra; B getExtra
B->C: B put Extra; C get extra
If I go to C, leave the App and then come back to the app, the app is in C, and if I try to navigate up to B, it crashes because I don't have the extras and the activity has been terminated.
What is the best behaviour? Keep them open? Recreate them?
the behaviour you're trying to achieve is the default behaviour on Android. So I believe that on your question, less is more.
AndroidManifest.xml:
remove all parentActivityName from it. It really does not much at all.
remove all launchMode those specific edge cases, unless you have a specific reason and a non-default behaviour, do not use them.
intent parameters you're passing to activities don't get messed by any of those manifest details, I don't think they have anything to do with your issue. On any activity you should be able to call getIntent().getExtras() and access any parameters passed to it, no matter how many times you exit and enter the app.
you do not need to save/restore the intent parameters during onSaveInstanceState and onRestoreInstanceState. Those callbacks are for current state, not for parameters passed to it. Those are different things.
back: that happens automatically. You cannot finish(); the previous activity when calling the next one.
up: it's just a matter of overriding the onOptionsItemSelected
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
finish(); // this finishes and the previous will be shown.
return;
// other menu items ?
}
}
if I try to navigate up to B, it crashes because I don't have the
extras and the activity has been terminated. What is the best
behaviour? Keep them open? Recreate them?
do NEVER say "it crashes" on StackOverflow without providing a stacktrace and the lines of code around that stacktrace. That's one of the main reasons I downvote people.
If it crashes, ask about that crash, do not mess-up the automatically-default navigation to "try to fix it"
If i understand you must set condition in second activity example on String data:
if(intent != null){
yourString = intent.getStringExtra("count");
}
then you must provide saving instance like
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("count", yourString);//or int or other look on developers
}
and restoring from instance
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
yourString = savedInstanceState.getString("count");
}
http://developer.android.com/design/patterns/navigation.html
The up button takes you to the previous activity hierarchically in the same app.
The back button takes you to the previous screen/app/activity that your phone was on, irregardless of which app it belonged to.
I have created an application that has multiple pages and navigation from one to another represents a crucial flow. I don't want the user to be able to press the back button and escape the activity without first warning him and then finally deleting all stack trace such that when the activity is launched again it starts afresh.
As of yet I have been using something similar to the function below :
#Override
public void onBackPressed()
{
this.finish();
Intent int1= new Intent(this, Home.class);
int1.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(int1);
super.onBackPressed();
}
But sometimes when after quitting the application when I launch it again it restarts from some random page or the one from where I quit the application (basically not the home screen from where it is expected to start)
I cannot think of a cleaner way to quit the application other than clearing all the previous activity flags as described in the code.
Any help on the above is appreciated!
EDIT :
Anytime during the flow of my activity if the user presses the back button, I want the control to be thrown back to the main page (clearing all the previous activity stack traces). Such that in case someone re-lanches the application it will re start normally from the main page.
You don't need any of this custom code in onBackPressed(). All you need to do is add this to all of your <activity> definitions in the manifest (except the root activity):
android:noHistory="true"
This ensures that none of your activities (expect the root activity) is recorded in the back stack. When the user clicks the BACK key, it will just return to the root activity.
Another benefit of this is that if the user leaves your app (by clicking HOME or by pulling down the notification bar and clicking on a notification, when he returns to your app it will also just return to your root activity.
Anytime during the flow of my activity if the user presses the back
button, I want the control to be thrown back to the main page
(clearing all the previous activity stack traces).
This can be done just by finishing all the activities as they move forward, except the MainActivity.
Such that in case someone re-lanches the application it will re start
normally from the main page.
Is it like if user is in Activity_5 and uses Home Button and relaunches the app again, MainActicity must appear?
IF so, you can call finish() in onPause() of every Activity except MainActivity
EDIT:
Might not be the perfect solution, but this is what I did to achieve exactly the same(logout in my application):
OnBackPressed() in any activity updates a boolean shared preference say backPressed to true and in onResume() of all the Activities, except MainActivity check its value and finish if true.
#Override
protected void onResume() {
super.onResume();
SharedPreferences mSP = getSharedPreferences(
"your_preferences", 0);
if (mSP .getBoolean("backPressed", false)) {
finish();
}
}
Back Button is used to go back to the previous activity. So i would not override the back button to clear activity stack. I suggest you use a Action Bar for this purpose. Navigate to Home Screen of the application using the application icon.
http://developer.android.com/guide/topics/ui/actionbar.html
Also check this link and comments below the answer by warrenfaith
android - onBackPressed() not working for me
#Override
public void onBackPressed()
{
moveTaskToBack(true);
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
you can use that code, it's work for me!
I have a problem for implementing up navigation on an app with this navigation tree:
The standard implementation of the back button is fine.
The problem start when trying to implement the Up button.
What I expect:
when the user is on Detail 5 Activity and press the up button the app goes to List 3 Activity
when the user is on Detail 7 Activity and press the up button the app goes back to Home Activity
So in different terms, I'd like to have this behaviour on the back stack:
The Android documentation (Implementing Ancestral Navigation) advice to use the following code to handle up navigation:
Intent parentActivityIntent = new Intent(this, MyParentActivity.class);
parentActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(parentActivityIntent);
finish();
But because the parent activity of the Detail Activity differs on the different navigation path I don't know which one it really is. So I can't call it in the Intent.
Is there a way to know the real parent activity in the Android back stack?
If not, is there a way to implement a correct up navigation in this app?
I will stick with my comment on Paul's answer:
The idea is to have a Stack of the last Parent Activities traversed. Example:
public static Stack<Class<?>> parents = new Stack<Class<?>>();
Now in all your parent activities (the activities that are considered parents -e.g. in your case: List and Home), you add this to their onCreate:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
parents.push(getClass());
//or better yet parents.push(getIntent()); as #jpardogo pointed
//of course change the other codes to make use of the Intent saved.
//... rest of your code
}
When you want to return to the Parent activity, you can use the following (according to your code):
Intent parentActivityIntent = new Intent(this, parents.pop());
parentActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(parentActivityIntent);
finish();
I hope am right (:
That's a tricky question and in my opinion really shows the difficulties in coping with the UX decisions of Android for the "up button". Therefore, there's not a clear-cut answer to your problem.
I have two possible solutions for you.
1. Mimicking the back button behavior.
You could consider adding an extra to the intent for launching Detail from one of its various parents. This extra would inform those activities which activity they would need to launch when android.R.id.home is pressed.
This would effectively mean that your app "goes back" to its common ancestor, instead of simply relaunching Home.
Another way of implementing this may be simply executing onBackPressed() instead of launching Home with Intent.FLAG_ACTIVITY_CLEAR_TOP, but bear in mind that the associated animation would be different than a normal "up" action.
2. Skip intermediate activites and go home.
Some apps treat the "up button" as a "home button". You might want to consider having it simply always relaunch Home with Intent.FLAG_ACTIVITY_CLEAR_TOP.
this is an old post for sure, but as I was studying the SharedPreferences, I think it could be a possibility to stack this information within a sharedPreferences data, and to modify its value each time before going down the 2 parents.
Then by reading it, you should be able to directly know your parent, and this without having to build a whole class for that.
Right now an activity gets destroyed when the BACK key is pressed. How can I make it just stop ( i.e. keep all the variables, etc. alive ), rather then be destroyed?
Thanks!
Why is it that you need to keep the variables alive? Given the established lifecycle of an Android application, I'm not sure that preventing the activity from being destroyed "just to keep the variables" makes sense.
Even if you stop the application without destroying it, there is always the chance that Android will kill it to free up memory. You will have to account for this in your code anyway, and so preventing the application from destroying doesn't save you from writing code.
Variables can be saved and restored relatively easily and quickly using SharedPreferences in your onPause() and onResume() methods. Unless you are storing a ton of data, preventing the application from destroying might not make much of a difference.
It sounds like you want to keep the variables in memory because you intend to return to this activity. Typically, you don't use the back button to navigate away from activities that you intend to come back to. Instead you would create an Intent and start a new activity. When you do this, Android places the current activity on the Back Stack calling onPause() and onStop(), which seems like exactly the sort of behavior you are looking for.
So if you still really want to prevent your activity from being destroyed (at least until Android decides it's using too much memory and kills it on it's own) you could always use Sagar's code and start a new activity in onBackPressed().
#Override
public void onBackPressed()
{
Intent intent = new Intent(this, Other.class);
startActivity(intent);
}
Just be certain that that is what you really want to do.
Simple one line
#Override
public void onBackPressed() {
mActivity.moveTaskToBack(true);
}
Pressing the BACK key triggers the onBackPressed callback method of Activity class. The default implementation of this callback calls the finish() method.
http://developer.android.com/reference/android/app/Activity.html#onBackPressed()
You can override this method to move the activity to background (mimick the action of pressing the HOME key.
eg:
#Override
public void onBackPressed() {
onKeyDown(KeyEvent.KEYCODE_HOME);
}
You could also instead consider moveTaskToBackground() mentioned here:
Override back button to act like home button
I have managed to work out exactly what you want: switch between 2 activities using Back button and keep them all not to be destroyed!
For example: you have 2 activities A & B. A will be started first, then A calls B. When B is loaded, user press Back button and switches back to activity A from B. From now B should not be destroyed and just goes to background, and when user starts activity B from A again, activity B will be brought to foreground, instead of being re-created again or created new instance! How to implement this:
1. Override onBackPressed() of activity B:
#Override
public void onBackPressed() {
Intent backIntent = new Intent(this, ActivityA.class);
backIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(backIntent);
}
2. On activity A, call activity B:
public void callActivityB() {
Intent toBintent = new Intent(this, ActivityB.class);
toBIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(toBintent);
}
remember to add flag: Intent.FLAG_ACTIVITY_REORDER_TO_FRONT when you call A&B.
This is similar to this question that was asked earlier.
Hope this helps!
N.S.
First of all, sorry for not answering the question, cause, I still have no optimal answer for it.
But, I really like when people start asking "what do you need this for". And, very rarely, the person who asked the question, really deserves this kind of question. I think not this time, but ok, this is not the issue...
Anyway, I will try to point out why some of us are convinced that
going from Activity A to Activity B(creating UI based on some data fetching) AND
going back from B to A(destroying all the created UI and/or fetched data in B) is sometimes a bad concept. Better solution would be to keep the stack as it is, so using something like finish() in Activity B, but keeping the Activity B in Pause state, so later when calling it again from Activity A - it just goes in onResume = nothing recreated in UI, no additional data fetching. The bonus of course is a fast and responsive UI, and the difference is really if you have a more complicated UI layout.
Just specify in the manifest for the activity as
android:persistent="true"
That should prevent your activity getting destroyed. To know more about this please refer to these below links which were answered by me
How to prevent call of onDestroy() after onPause()?
Prevent activity from being destroyed as long as possible
In the above posts I have explained in detail with a use case