My Android app comprises several activities: M (main or root), A, B, C...
Below is a possible activity navigation graph:
When my root activity M is being initialized, I cache some parameters (like screen dimensions) as static variables in special class MyUtils to use them later in other activities.
The Kaboom happens when I press Home button in activity say C and then launch a dozen applications. When I return back to my application, it appears that everything has been destroyed. C.onCreate method is being called, but cached parameters appears to be reset.
I would like to start from M, not from C after Android has devastated my application after a long pause. How can I achieve this?
I thought of something like this:
// to be put into all my activities but M:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (MyUtils.GetScreenWidth() == -1)
{
// seems like Android killed my app
finish();
return;
}
// Normal initialization.
// Use MyUtils.GetScreenWidth() to align my ui elements.
}
...but I'm not sure that it's the best way. What would you suggest?
To be honest, I would do the same or something similar to what you're doing. A possibly better idea is to have a static MyUtils.initialize() method, perhaps taking in an application context parameter, that is called at each onCreate() of each Activity that uses MyUtils.
Either that, or store the values each in a SharedPreference.
You probably don't want to do things this way unless you require the user to interact with the front-door activity (for example, to authenticate the user again).
If you do want to force them to come back in through that front door activity, you can use an Intent to launch it when you detect that you have started up in a new process with one of the other activities. You will probably want to spend some time reading through the documentation for the Intent flags to select the ones which apply for this usage.
It's not a really smart mean but you could store these information in some other place like a database or simply a file then retrieve it when needed.
Related
I wondered if anyone can shed some light on this,
I have one activity app which has a listview. In onCreate() I populate some data in that listview (reading it from sqlite database). I have a close button which triggers finish(); too. now when I press close button, activity disappears but when I click on app icon on desktop (or selecting it from phone history button) I see all previous data in the listview. the function that I am looking for is to start app fresh after clicking close button for next run. (something like banking app log-out button). (list view here is only an example to put across the need, so clearing list-view before finish(); is not acceptable. It is very simple and plain request and I do not think any code is necessary but if anyone interested I will post some code too.
What I want is same behavior as a banking app in exit, when user leave the main screen or click sign out, the App closes altogether. I can achieve this by using following methods (number 2 and 3) but apparently these solutions are not best practices. Then what method a banking App uses to close the app instantly? I doubt they use something which is not best practice such as System.exit(0)?! or do they!
Many developers claiming closing an App or killing parent activity
is OS job
Some say use use :
int pid = android.as.Process.myPid();
android.os.Process.killProcess(pid);
(this solution according to this is not a good idea because in next run app acts like it has been crashed last time https://stackoverflow.com/a/24015569/4173238 )
some say use System.exit(0); according to this
https://stackoverflow.com/a/5846275/4173238 is not recommended either
some say use finish(); but finish does not do what I want
Thanks for any input
If you have a mechanism that allows it so that you only deliver messages to the main thread when the application is resumed, then you can register your activities on an event bus like Otto and send an event that every Activity is subscribed to on which they call finish() on themselves.
Another possible solution is starting every activity with startActivityForResult(), and if you click the exit button, then you would say
public static final int KILL_ACTIVITY_RESULT_CODE = 0xD34DB33F; //why not
public boolean onOptionsMenuItemSelected(MenuItem menuItem) {
if(menuItem.getId() == R.menu.kill_activity) {
setResult(KILL_ACTIVITY_RESULT_CODE);
finish();
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == KILL_ACTIVITY_RESULT_CODE) {
setResult(KILL_ACTIVITY_RESULT_CODE);
finish();
}
}
}
...and one time I've seen someone make static references to every single activity they had, and called finish() on each and every one of them. But don't do that, that essentially means you have failed as an Android programmer, and there is nothing to redeem you of your sins.
As brilliant CommonsWare, has pointed out in his comment "Static" was the issue! I was using static variables to store data to fill My listView. Apparently even if you have only one Activity and close it, Static variables remain intact! on app re run!
If you asking why I used static variable at the first place, I have to say, right or wrong, I wanted to share that variable between my other java class (my databaseHandler.class).
Why Android not clear all (including static variables) resources when closing the main and only Activity of app, remains a question and this is my next reading topic! but many thanks for anyone who post a comment on this question,
I will also change the question from:
How Banking Apps close? finish() does not do the same job for me
to
closing an activity using finish(); wont make app start fresh in next
run! why?
In my App there is three activities: A, B, and C. Activity A is the main activity.
When I start Activity B from A using Intent, A is automatically destroyed. However, I am not finished with A when I start B. Then, when I press the back button in the device the A is started using onCreate and I've lost all my data since I am not using an Intent to go back.
The same happens when opening C from B.
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
startActivity(new Intent(ActivityA.this,
ActivityB.class));
}
});
What's going on? How do I fix this?
This was a mistake by me due to this the Activities were automatically destroying. I Checked the option under Setting=>Developer Option=>Don't Keep Activities. Due to that destroyed every activity as soon as the user leave it.
By Unchecked this option my app working fine.
If you are using 'Activity B' to get some sort of one-off result then it sounds like you need to be using the startActivityForResult construct. See [the docs](http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int))
However, to maintain state information about 'Activity A' without persisting it anywhere you want to be bundling all the information (fields the user has filled in etc.) into the Intent bundle passed to 'Activity B'. This bundle is delivered to 'Activity B's onCreate (and a few other lifecycle points) where you can continue populating any pertinent information from 'Activity B' and writing that information back to the intent bundle before restarting 'Activity A' with the modd'ed bundle when you are done with 'Activity B'. But here be dragons. This specific mechanism is employed in our app with a strict (wrong?) control over the back-stack. Specifically our 'Activity A' has the special manifest declaration of android:noHistory="true". This means that we also control what happens when the back button is pressed by overriding onBackPressed() at each activity and sending that same bundle through to a new instance of the activity we just came from. It looks like a normal back stack but since we are wanting to remember user input without persisting that information anywhere then it's a usable solution.
This allows for a wizard like input with each stage of the wizard input being it's own activity and only validated and correct information being persisted at the end.
An alternative would be to temporarily stored 'Activity A's data in the SharedPreferences area so that on restarting Activity A you can pull out the appropriate bits reassembling the original state of 'Actvity A' when you left it. Rest assured there is very little you can do when Android wants to kill your Activity A but you can accommodate it's very reasonable killing with the approaches outlined here.
Maybe it is not you that is destroying your activity.
From the page about Activity Life cycle: (Emphasis mine)
If an activity is paused or stopped, the system can drop the activity
from memory by either asking it to finish, or simply killing its
process. When it is displayed again to the user, it must be completely
restarted and restored to its previous state.
There explicit says that the system can kill your activity process.
Some possibilities is your device is low on memory, or your activities is memory hungry, making the system kill it.
Edit
In order to maintain your data, between the activities even if it gets destroyed. One way is to use the Application class, check this answer on Using the Android Application class to persist data, maybe that's what you're looking for.
I have an Activity that should only get created once. That is, onCreate can only be called once. If it's called again, I want the Activity to do nothing.
Is it advisable to do the following?
protected void onCreate(Bundle savedInstanceState) {
this.setTheme(android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
super.onCreate(savedInstanceState);
if(onCreateWasCalledAlreadyBoolean) {
setResult(RESULT_OK);
finish();
return;
}
//Do other stuff here
}
I assume you understand how the activity life cycle works. I mean, you are not trying to avoid something that does not apply here (thinking that onCreate may be called multiple times whenever it just onRestarts etc.).
Technically, it's perfectly fine.
However, you should be worrying more about why you need to call your activity ("A") again if it shouldn't be created at all, if that's what you're thinking.
If you've caught yourself checking if your activity A was already "called" (?), this could mean the previous activity ("B") has a mistake in the logic flow of the app, and that B instead should be checking if it must in fact start that activity A. I mean, if you need to decide if you must call an activity, check before starting it.
I don't think that's applicable if you're restarting the activity (e.g.: go Home, then navigate back), but then again you should be restarting it from where you left (B for what I can tell). You won't be navigating back to A. And you didn't give much detail, so I'd guess this is some kind of splash screen, like evilmage93 said.
If that's indeed some kind of splash screen, I would advise to show it whenever the user navigates back all the way to remove your app from the task stack (contrary to his advice). In other words, whenever the user restarts the app from its "front door".
Although that's ultimately a design decision, I prefer to see the splash screen whenever the app is being loaded ("entered") in the stack for the first time, and it should work fine if you (obviously) finish A before calling B (the splash screen is supposed to finish itself when done, even in its first run). It's a matter of consistency: the same app should behave the same way whenever the user performs the same task (start app from its "front door").
Still, I answered your question covering some general aspects because you asked in such way.
// edited:
Finally, by looking at that onCreateWasCalledAlreadyBoolean I'm afraid you may be trying to reinvent part of the activity life cycle mechanism. In this case, don't: proceed with your regular activity logic because the user expects that behavior. Generally I wouldn't advise people to break the normal loading of an activity just because it was killed and restarted by the system.
I don't see why not. Wouldn't it be simpler to not restart the activity at all though?
What are you worried about NOT being okay? Performance..Uncaught exceptions..Code clarity?
I have app that connects to internet to get data. I can access data multi-level.
So let say I start at level 3 and on level 4 I decide to go back, whenever I press back the previous activity reloads the data from the internet.
Is there any possibility to prevent that?
I have tried to run the activity in single-top mode.
Move the data loading code to the single-exec event: onStart or
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
// here
}
}
You have a lot of options here.
The simpler one is to avoid stopping your activity when you switch to another one. When you go to level 4 by means of startActivity(), your onPause() is called on the level 3 activity but it continues living in the background. Then when you come back from 3, onResume() is called.
So as long as you don't load data on resume nor finish() your level 3 activity, you should be fine. If the system happens to be very short on resources then level 3 activity might be killed (though this is very rare) and restarted when you get back to it, but if it happens it means the system needed memory for something important and it's probably fine to reload.
Now, there are other ways. It's usually much better to do it as I described above, but if for some reason you want to finish your level 3 activity, Here are your options.
As it has been noted, you may elect to dump your data somewhere. The saved instance state is an option - though if it's heavy data, more than a few kilobytes, it's not recommended. The idea is, you save your data in onSaveInstanceState() in the Bundle and restore it in onCreate().
If it's heavy data, you would be better off dumping it in a cache file.
If you have a data model, and want to use the same data across several activities, maybe a widget and possibly even a different app, you may want to consider building a ContentProvider to supply the data. It would live independently of the other parts of your application and manage access to the data. Other parts would query it for the data they would need.
The neat thing about that is, it abstracts the data away from the rest of the program. It can be accessed from anywhere and caching policies and everything is handled in a dedicated place. The drawback is, it's significantly more complicated.
One possible solution would be to work with states. You basically have a boolean which indicates if your activity has performed a certain action.
If the action was performed you it won't reach that code again. Of course that flag must be saved somewhere in the Application context or in SharedPreferences.
You can add this parameter to your intent to prevent reloading.
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(new Intent(this,My.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
So I have the following:
A Common class that many of my Activities access in my android application, via setting the class in my manifest:
<application android:name="com.dev.games.phraseparty.Common"... />
Now, within this class I have several objects that are constructed to preserve application state and common services to perform applications that are constructed in the Common constructor
ie
GameStateVO gameState;
public Common()
{
gameState = new GameStateVO();
}
My problem is that my Activity has an Admob ad. When the user clicks on the ad, it calls the webbrowser intent to open the ad URL in a webbrowser.
Now, when I click back from the webbrowser launched by admob, it takes me back to the caling activity and the OnCreate method is called.
This activity then gets a null pointer exception because it will do something like:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Common common = this.getApplication();
//null pointer here since the game state VO is null since the Common has lost its state.
int score = common.getGameState().getScore();
}
If you have no active foreground activity, then your process is ripe for shutdown by the OS to obtain more resources. The browser app in particular i've noticed uses a lot of resources and can quickly lead to background activities and later processes being killed off. Having a service can help keep your process around, but even then it can still be killed off if needed. I think you'll need to use the activity lifetime cycle method to save & restore your state. See the process lifecycle section of the docs for more info
You might want to look into implementing the onSaveInstanceState method: this lets you store any relevant information before it gets killed off. See http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState%28android.os.Bundle%29 for the actual call you need to implement, and Saving Android Activity state using Save Instance State for a quite excellent example.