I have 2 activities, a 4 digit pin style login Activity (MainActivity) and a content Activity.
This app stores private information and such that should not be able to be seen when resuming an activity, therefore i want the app to completely restart on the login activity each time it's launched, rather than picking up where it left off.
I've tried messing around within the content activity's onStop() and onResume methods, but these seem to be highly unreliable as sometimes when i have the onRestart setup to relaunch the login activity, it won't launch it at all, and calling for the login screen to be activated in the onStop() will prevent me from being able to finish up tasks in the background, such as saving data.
Is there anything i can add to the manifest file that will tell the app to restart from the login activity no matter what? One of the worst things that can happen when working on an app like this is that the information is accessible to someone other than the owner that wasn't forced to login..
What you need is to focus on two calls in your Content Activity:
onPause(): This is where you kill Content activity and remove it from the stack. You can do this easily:
#Override
public void onPause() {
super.onPause();
//Save your data here
finish(); //Kill Contect Activity.
}
onRestart(): This is where you redirect the user to the Main Activity.
#Override
public void onRestart() {
super.onRestart();
Intent i = new Intent(this, MainActivity.class);
startActivity(i);
finish(); //Kill Contect Activity.
}
This graph might help you in understanding the Activity lifecycle:
What you need is setting the flag android:clearTaskOnLaunch and android:excludeFromRecents for your activities in the Manifest.
Related
I have an application with basically a list of items and a detail screen for each items.
When initially started, we show the list of items. If the user switches to another app when viewing a details screen, when he comes back to the app, the details screen is shown.
All that is the standard, and working well. However, my client needs the user to come back to the list screen instead of the details screen each time the app is resumed.
My first idea would be to remember the time at which the details activity got paused, and when started, if the time is greater than X seconds, finish and launch list activity instead of resuming.
Any more reliable way to do that?
PS: I know we should not do that, I already explained that to my client, decision is not mine.
Use SharedPreference to save the time of your paused detail activity in onPause and when it resume check the saved time with current time whether it has passed your threshold if it is passed then close it otherwise remain it opened.
Implement this solution and it definitely helps.
Basically, the app is not actually restarting completely, but your launch Activity is being started and added to the top of the Activity stack when the app is being resumed by the launcher. You can confirm this is the case by clicking the back button when you resume the app and are shown the launch Activity. You should then be brought to the Activity that you expected to be shown when you resumed the app.
The workaround I chose to implement to resolve this issue is to check for the Intent.CATEGORY_LAUNCHER category and Intent.ACTION_MAIN action in the intent that starts the initial Activity. If those two flags are present and the Activity is not at the root of the task (meaning the app was already running), then I call finish() on the initial Activity. That exact solution may not work for you, but something similar should.
Here is what I do in onCreate() of the initial/launch Activity:
if (!isTaskRoot()
&& getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
&& getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_MAIN)) {
finish();
return;
}
for more details on isTaskRoot()method reference.
You have to provide the following onPause() method to all the activity classes except your list_item activity(Initial Activity).
#Override
protected void onPause() {
super.onPause();
Intent i = new Intent(getApplicationContext(), list_item_activity.class);
startActivity(i);
}
I might understand your problem incorrectly but why you do all the timing stuff? I mean, assuming you've fragments for your list and detail views, just put a a flag to monitor your activity has stopped and listen to catch your activity resume ( via onResume or onWindowFocusChanged ). If it's stopped and resumed then transition to list fragment if it's not already visible.
You can you a broadcast receiver here.
And on activity OnResume method use a call to broadcast receiver and perform whatever you need like this.
#Override
protected void onResume() {
super.onResume();
sendBroadcast(new Intent("YourActionHere"));
}
I have two activities; let's say A and B. In activity A there is a broadcast receiver registered that listens for a particular event which will finish activity A. I am registering the broadcast receiver in onCreate(), and destroying it in onDestroy() of activity A.
For simplicity, there is one button in activity B named "Destroy Activity A". When a user clicks on button, activity A should be destroyed.
Normally all of this is running smoothly without any issues,but the problem occurs in following scenarios:
1) Suppose I am in activity B and i press the Home key to move the application to the background then if i use other resource-heavy applications, Android system will kill my application to free memory. Then If I open my application from recent tasks, activity B will be resumed, and it's onCreate(), onResume() etc method will be called. Now I press button to destroy activity A, but activity A has already been destroyed, so activity A's onCreate(), onResume() etc methods will not be called until and unless i go to activity A by pressing the back button. Thus broadcast receiver is not registered to listen for the event.
2) The same problem will arise when user has selected "Don't keep activities" from Developer options in the device's settings.
I have been looking to solve this issue for a long time, but i am unable to find a proper answer. What is the best way to handle this scenario? Is this an Android bug? There should be some solution for this issue.
Please help me.
If your Activity A has destroyed by Android OS itself then there are
no way to track.
Some people has suggested to track that Activity A by listning event in onDestroy method BUT if your Activity killed by system OS then note here it wont call those method .
This cannot be fixed while keeping your current broadcast logic.
Killing activities from the backstack, imo, is not a correct approach. You should strongly consider changing the logic of your navigation.
But if your project is big and time is a problem, and refactoring is out of the question, A.J. 's approach works, but you mentioned that you have lots of activities that needs to be killed, his solution becomes very tricky to implement.
What I suggest is the following. This might not be the best idea, but I cannot think of another. So maybe that could help.
You should have the following:
A Base Activity for all your activities.
A ArrayList<String> activitiesToKill object at the application level. (If you did not extend Application you can have it as static variable
First we have to make sure that the activitiesToKill is not lost when the OS kills the app in low memory. In the BaseActivity we save the list during onSaveInstanceState and restore it in the onRestoreInstanceState
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("activitiesToKill", activitiesToKill);
}
private void onRestoreInstanceState(Bundle state) {
if (state != null) {
activitiesToKill = (ArrayList<String>) state.getSerializable("activitiesToKill");
super.onRestoreInstanceState(state);
}
}
The idea here is to save which activities should be killed in the list, by using their name.
The logic is as follow:
Let's say you have Activities A, B, C, D and E
From Activity E, you press the button and you want to kill B and D
When you press the Button in E, you add the names of B and D to the activitiesToKill object.
activitiesToKill.add(B.class.getSimpleName()
activitiesToKill.add(D.class.getSimpleName()
In the onCreate method of the BaseActivity, we have to check if the
if(savedInstanceState != null)
{
//The activity is being restored. We check if the it is in the lest to Kill and we finish it
if(activitiesToKill.contains(this.getClass().getSimpleName()))
{
activitiesToKill.remove(this.getClass().getSimpleName())
finish();
}
}
Make sure to remove the name of the activity if it is killed through the broadcast.
So basically this is what happens in every scenario.
If the app is running normally, and you click the button, the broadcast gets sent and B and D will get killed. Make sure to remove B and D from the activitiesToKill
If the app was killed and restored, you press the button, the broadcast will have no effect, but you have added B and D to the activitiesToKill object. So when you click back, the activity is created and the savedInstanceState is not null, the activity is finished.
This approach consider that activity E knows which activities it has to kill.
In case you DON'T know which activities to kill from E, you have to modify this logic slightly:
Instead of using an ArrayList use a HashMap<String, bool>
When Activity B is created, it will register it self to the hashmap:
activitiesToKill.put(this.class.getSimpleName(), false)
Then from Activity E, all you have to do is set all the entries to true
Then in the on create of the base activity you have to check if this activity is registered in the activitiesToKill (the hashmap contains the key) AND the boolean is true you kill it (don't forget to return it to false, or remove the key)
This ensure that each activity register itself to the HashMap and Activity E doesn't have top know all the activities to kill. And don't forget to remove them in case the broadcast kills them.
This approach also ensure that the activity is not killed when opened normally from an intent because in that case onSaveInstanceState would be null in the onCreate, so nothing will happen.
More advanced checks can be accomplished in case you have groups of activities that needs to be terminated through different conditions (not only a button click) so you can have a HashMap of a HashMap to divide them in categories.
Also note, that you can use getName instead of getSimpleName if you have multiple activities with same name but different bundles.
I hope my explanation is clear enough as I wrote it from my head, let me know if any area is not clear.
Best of luck
One of the main rules with Activities is you can't rely on any activity being alive except the foreground activity. The thing you're trying to do with broadcasts has nothing to do with back stack -- back stack doesn't guarantee all activities are alive at all times, but it will make sure they're recreated when it's time to go foreground.
In your example (if my understanding of what you're aiming to do) you need to navigate to something underneath A -- say, Activity Z, and the stack looks like this: Z-A-[B]. There's normal course of events where you hit back and it takes you to A, then after another hit -- to Z but in a certain case (say pressing a button) you want to move back to Z bypassing A -- this is a classic case to use FLAG_ACTIVITY_CLEAR_TOP and launch Z explicitly:
Intent intent = new Intent(this, ActivityZ.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
This will finish both B and A, and deliver the intent to Z. You will, probably also need the FLAG_ACTIVITY_SINGLE_TOP flag, pay close attention to the description of FLAG_ACTIVITY_CLEAR_TOP, there's some trickery you should consider.
I don't know if it's possible to handle this on a "proper" way.
What it comes to my mind, is to flag the A activity in some way. You can't use startActivityForResult() because you will receive the result before onResume() is called i.e. UI was already inflated.
If you using an Otto, you could try with a sticky event. Otherwise you will need a singleton to handle the flag or save it to shared preferences.
You will have to check that flag on your onCreate() method before calling setContentView(), if the flag is true just finish the activity.
With the information you have given, how about you register the broadcast in onCreate of Activity B after checking if its registered already or not. If onDestroy of Activity A has been called in either of the scenarios you have mentioned, then the deregister of the Broadcast would have been called. So in that case, you can register your Broadcast in onCreate of Activity B, so that you can listen to it, even if you have only the Activity B in your backstack.
Have you considered using Sticky Broadcast?
Also you can register your receiver on application level (in manifest) and listen to this event regardless of Activity A state.
But, like already said Youssef, killing activities from the backstack is not a correct approach. You should strongly consider changing the logic of your navigation.
Many solutions came to my mind but as you have not provided much information about your app, so I think this should work in general.
Instead of firing a broadcast to kill Activity A, just execute the following code when the "Kill Activity A" button is pressed in Activity B.
Intent intent = new Intent(getApplicationContext(),
ActivityA.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.putExtra("EXIT", true);
startActivity(intent);
Add the following code in activity A
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.getBooleanExtra("EXIT", false)) {
finish();
}
}
protected void onCreate(Bundle savedInstanceState) {
//Ideally, there should not be anything before this
super.onCreate(savedInstanceState);
if(getIntent().getBooleanExtra("EXIT", false)){
finish();
return;
}
In the manifest set "singleTop" launch mode for activity A.
<activity
android:name=".ActivityA"
...
android:launchMode="singleTop" />
This will have the following consequences:
If Activity A is already running it will brought to the front of the activity stack and finished, thus removing it from the stack.
If Activity A has been destroyed but still present in the activity stack (to be launched when back button is pressed), it will be started, brought to front and finished, thus removing it from the activity stack.
If Activity A has already has already been destroyed and not present in the activity stack, and you still press the "Remove Activity A" button, it will be started, brought to front and finished.
Generally, you should not see any flicker.
Based on this idea, you may build a better performing solution for your particular app. For example, you may use the FLAG_ACTIVITY_CLEAR_TOP and finish the Activity A in onBackPressed() of Activity B.
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
My application starts with a welcome screen Activity, but that screen has an option to skip that screen altogether in future launches.
What's the proper Android way to do this? Initially, I just automatically detected the skipWelcome preference and switched to the 2nd activity from Welcome. But this had the effect of allowing the user to hit the back button to the welcome screen we promised never to show again.
Right now, in the Welcome activity, I read the preference and call finish() on the current activity:
SharedPreferences preferences = getPreferences(MODE_PRIVATE);
boolean skipWelcome = preferences.getBoolean("skipWelcome", false);
if (skipWelcome) {
this.finish();
}
And then I implement onDestroy to move on to the next Activity:
#Override
public void onDestroy() {
super.onDestroy();
startActivity(new Intent(Welcome.this, StartFoo.class));
}
But this makes for some weird visual transitions. I'm starting to think that I need a base Activity that pops open Welcome only if proper, and then goes to StartFoo.
I can't comment on Mayra's answer or I would (not enough rep), but that's the correct approach.
Hidden in the Android documentation is this important phrase for Activity.startActivityForResult(),
"As a special case, if you call
startActivityForResult() with a
requestCode >= 0 during the initial
onCreate(Bundle
savedInstanceState)/onResume() of your
activity, then your window will not be
displayed until a result is returned
back from the started activity. This
is to avoid visible flickering when
redirecting to another activity."
Another important note is that this call does not block and execution continues, so you need to stop execution of the onCreate by returning
if (skipWelcome) {
// Create intent
// Launch intent with startActivityForResult()
return;
}
The final piece is to call finish immediately in the welcome activity's onActivityResult as Mayra says.
There are a few solutions to this.
Did you try just launching the activity and finishing? I vauguely remember that working, but I could be wrong.
More correctly, in if(skipWelcome) you can start the new activity for result, then when onActivityResult is called, immidiately finish the welcome activity.
Or, you can have your launcher activity not have a view (don't set content), and launch either the welcome activity or StartFoo.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Quitting an application - is that frowned upon?
I want to offer the user an option to exit the application as I need to delete some sensitive data, which is stored in the SharedPreferences as long as the application needs it.
As soon as the user wants to exit, the password in the SharedPreferences should be wiped and of course all activities of the application should be closed (it makes no sense to run them without the known password - they would crash).
How can I do that?
System.exit(0) and finish() only exit the current activity - useless. I know there is a taskmanager app. How is that one doing it? It's able to kill the whole application...
When you use the finish() method, it does not close the process completely , it is STILL working in background.
Please use this code in Main Activity (Please don't use in every activities or sub Activities):
#Override
public void onBackPressed() {
android.os.Process.killProcess(android.os.Process.myPid());
// This above line close correctly
}
You are correct: calling finish() will only exit the current activity, not the entire application. however, there is a workaround for this:
Every time you start an Activity, start it using startActivityForResult(...). When you want to close the entire app, you can do something like this:
setResult(RESULT_CLOSE_ALL);
finish();
Then define every activity's onActivityResult(...) callback so when an activity returns with the RESULT_CLOSE_ALL value, it also calls finish():
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(resultCode)
{
case RESULT_CLOSE_ALL:
setResult(RESULT_CLOSE_ALL);
finish();
}
super.onActivityResult(requestCode, resultCode, data);
}
This will cause a cascade effect closing all activities.
Also, I support CommonsWare in his suggestion: store the password in a variable so that it will be destroyed when the application is closed.
When the user wishes to exit all open activities, they should press a button which loads the first Activity that runs when your app starts, in my case "LoginActivity".
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("EXIT", true);
startActivity(intent);
The above code clears all the activities except for LoginActivity. LoginActivity is the first activity that is brought up when the user runs the program. Then put this code inside the LoginActivity's onCreate, to signal when it should self destruct when the 'Exit' message is passed.
if (getIntent().getBooleanExtra("EXIT", false)) {
finish();
}
The answer you get to this question from the Android platform is: "Don't make an exit button. Finish activities the user no longer wants, and the Activity manager will clean them up as it sees fit."
which is stored in the
SharesPreferences as long as the
application needs it.
Why?
As soon as the user wants to exit, the
password in the SharedPreferences
should be wiped and of course all
activities of the application should
be closed (it makes no sense to run
them without the known password - they
would crash).
Even better: don't put the password in SharedPreferences. Hold onto it in a static data member. The data will naturally go away when all activities in the app are exited (e.g., BACK button) or otherwise destroyed (e.g., kicked out of RAM to make room for other activities sometime after the user pressed HOME).
If you want some sort of proactive "flush password", just set the static data member to null, and have your activities check that member and take appropriate action when it is null.
Using onBackPressed() method:
#Override
public void onBackPressed() {
android.os.Process.killProcess(android.os.Process.myPid());
}
or use the finish() method, I have something like
//Password Error, I call function
Quit();
protected void Quit() {
super.finish();
}
With super.finish() you close the super class's activity.
My understanding of the Android application framework is that this is specifically not permitted. An application is closed automatically when it contains no more current activities. Trying to create a "kill" button is apparently contrary to the intended design of the application system.
To get the sort of effect you want, you could initiate your various activities with startActivityForResult(), and have the exit button send back a result which tells the parent activity to finish(). That activity could then send the same result as part of its onDestroy(), which would cascade back to the main activity and result in no running activities, which should cause the app to close.