I have an activity A which shows different screens. I chose to show multiple views in the same activity, by using setContentView.
When you launch the app, Activity A shows the default screen. You then choose an option and the activity shows you another set of options by setting setContentView to another view. This new view now takes you to a new activity(Activity B) when you click it, and Activity A calls finish()
If I now end Activity B, by calling finish() on it, I am being taken back to Activity A, and it calls it's onCreate(). I can't figure out what's wrong here. Why would an activity recreate itself when Activity B never called an intent for Activity A?
view.findViewById(R.id.quit).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
The issue is most likely because the purpose of onCreate() is to create the Activity's state as it was when it left. You can't under any circumstances assume that an Activity will maintain its state once the user leaves it. Views are memory intensive, and the system is going to dump it as soon as it needs more memory for the current top Activity. If this happens then the system must recreate the Activity to its original state before the system dumped it.
You can save states via the onSaveInstanceState(Bundle) method. The bundle that's passed in to onCreate is the same bundle you set in onSaveInstanceState(), so all parameters will be there. Then you can rebuild just as it was.
Related
Suppose you have an app that has two activities: activity A (the launcher) with a button that launches activity B. The code of activity B that matters is:
final Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
#Override
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView(....);
Thread.setDefaultUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler()
{
#Override
public void uncaughtException( Thread thread, Throwable ex )
{
}
});
}
Activity B loads some bitmaps for its interface. It does NOT restore the old default uncaught exception handler on its exit. Doing A->B->A->B... causes an out of memory exception, because activity B is leaked. Why exactly activity B is leaked?
In my opinion this is the wrong architecture. For chained search you should only ever have a single instance of each Activity. You should flip between the different Activity instances by calling startActivity() and setting Intent.FLAG_ACTIVITY_REORDER_TO_FRONT in the Intent you use. Also add the data you want to display as "extras" in the Intent.
To be able to use the BACK button to back through the chain (no matter how long it is), each Activity should manage a stack that contains the data that it needs to recreate the page whenever the user backs into it. In onCreate() and in onNewIntent() the data (from the "extras") should be pushed onto the stack and displayed. You then override onBackPressed() and go back to the previous Activity by calling startActivity(), and setting Intent.FLAG_ACTIVITY_REORDER_TO_FRONT in the Intent you use. You also add an "extra" to the Intent that indicates the user wants to "go back". In onBackPressed() you should also discard the top element off the data stack of the Activity that is being left. This will ensure that the stack is correct when the user then backs into this Activity.
In onNewIntent() if the user just backed into the Activity, you just display the data that is already on top of the stack of managed data.
In this way, you only ever have one instance of each Activity, the user can chain all day through the set of activities and the BACK button always works and you don't have to worry about running out of memory.
Trying to accomplish this using taskAffinity or Intent flags or similar will not work. Don't waste your time. It is also bad programming style.
I hope this is clear.
and for memory exception useadd manifest
android > v3
<application
....
android:largeHeap="true">
This is a follow up question to this question:
How to resume activity instead of restart when going "up" from action bar
I got 2 activities, both are defined in the manifest with android:launchMode="singleTop".
Activity A calls activity B. Activity B got a back button:
backButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
finish();
}
});
On some devices there is no problem and the previous activity resumes. Other devices are restarting the previous activity instead of resuming it. Why is that?
This could be because on some devices your activity is being garbage collected to save memory. If you want to resume instead of restart even in the case of android killing your activity override the onSaveInstanceState method, and save all your variables in the bundle.
Now when you go back to your activity, if it was killed then in on create a bundle will be passed called savedINstanceState. If savedInstanceState is not null then use the data present in it to bring your activity back to the previous state.
more details here: How to use onSaveInstanceState() and onRestoreInstanceState()?
basically my app has 2 activities.Say "A" and "B"
.
A launches B.
Activity B plays music and also has a notification.
Case 1:when the view is still on activity B` i press home button and then i click on the the notification, activity B is opened with its view intact and with its music playing (because in the manifest i am using android:launchMode= "singleTop" and hence another instance of the activity is not created) this part is as desired......
but
Case 2:when the view is on activity B and i press back button ,activity A appears and then i click on the the notification, activity B is opened with the view lost and music also stops(not desired)......i am making a guess that it happens because when i press the back button the activity is destroyed ,so i have to progamatically restore its view right??so to restore its view i override two methods .....
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putBoolean("boolMusicPlaying", boolMusicPlaying);
savedInstanceState.putInt("swapnumber", swapnumber);
savedInstanceState.putString("seekbarprogress", progress2);
savedInstanceState.putInt("position.seekbar",seekbar.getProgress());
savedInstanceState.putString("seekmaxString", max2);
savedInstanceState.putInt("seekmaxInt",seekMax);
savedInstanceState.putParcelableArrayList("songfetails",songdetails);
super.onSaveInstanceState(savedInstanceState);
}
//make a note ....even if i don't override onDestroy() and don't call on SaveInstanceState explicitly, then too i am not getting any desired effect.......
#Override
public void onDestroy()
{ Bundle savedState = new Bundle();
onSaveInstanceState(savedState);//because of this line....the app is crashing......
super.onDestroy();
}
but it didn't help.....
and in on create i am putting a check if saved instances is null or not....create view accordingly...(i.e from saved instances or from fresh) but it didn't help...
also this line is giving a crash ...onSaveInstanceState(savedState);
even if i don't override ondestroy() and kill the app manually from task killer,and then try to open activity B,then too the saved instances thing should work right,because the method OnSaveInstanceState will be automatically called then,right???please help
Basically if you have pressed a back button,restoration of activity should be done using shared preferences/or a database but if you haven't pressed the back button and then you want to restore the state of the activity (because the activity was destroyed by the system ) then the bundle savedinstances can be used ...
From Android documentation:
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. 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.
To fix second problem you must restore data form some other source (service that plays music, restore info from application context or singleton, etc.)
For more info check this.
I am developing an application in which i am overriding the back button. I created a check box. On click of which i am calling intent for:
startActivityforResult();
And also maintaining the state of activity as :
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean("checkbox", checkbox.isChecked());
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
checkbox_state = savedInstanceState.getBoolean("checkbox");
}
which run fine and the state is maintained.
Means i am entering value in edit text. and on check box click calling new activity for result and while return on first activity the state is maintain.
Now but from second activity if i click on device back button the state is not maintained.
So what should i do to maintain the state on back button. I searched but not found satisfied solution. Please suggest me.
When You start the new Activity then the current Activity gets hidden and new Activity is placed on top of the stack. Now if you press the back button on the new Activity then the first activity should come up in its current state (If it wasn't destroyed, calling finish()) where it was left i.e. if the check box was checked then it should remain checked. You don't need to save the activity state unless the orientation is changed or the activity is destroyed.
Are you sure you are not doing any thing in the onActivityResult or onResume() method which effects the state of the check box? I would recommend to first comment out all the code in both the methods and see if your check box retains the state. Also can you also make sure that the code itself doesn't uncheck the checkbox before starting the new Activity?
Now but from second activity if i click on device back button the state is not maintained.
onSaveInstanceState() is mostly used for configuration changes (e.g., rotating the screen).
So what should i do to maintain the state on back button
Most likely, there is no "state" that needs to be "maintained", outside of your data model. Your activity needs to update its data model: files, database, preferences, ContentProvider, some singleton that is your in-memory data model manager, whatever.
The books I have are abysmal at explaining how to work with the lifecycle, there's a lot I'm missing that I'm hoping somebody can fill in.
My app structure is that when it's first started, it starts an activity full of legalbabble that the user has to accept. When he says 'ok', I start my main activity and then I call finish like this:
public void onClick(View view) { //as a result of "I accept"
Intent mainIntent = new Intent(mParent, EtMain.class);
startActivity(mainIntent); // Start the main program
finish();
}
Then in EtMain in the onCreate method, I've got some tabs and I instantiate some classes:
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
SetupTabs.setMyTabs(mTabHost, this);
mComData = new ComFields(this); // Create the objects
mDepWx = new WxFields(this, DepArr.Departure);
mArrWx = new WxFields(this, DepArr.Arrival);
mDepVs = new DepFields(this);
mArrVs = new ArrFields(this);
mTabHost.setOnTabChangedListener(new OnTabChangeListener(){
}
Questions:
The 'finish' in the first fragment should terminate the legalbabble activity so it'll never be restarted, right? And the EtMain one will remain forever (until killed externally), even if my app gets pushed to the background, right?
The way it is now, when EtMain gets pushed and later brought to the foreground (by tapping on the icon), it goes through the legalbabble screen as though it's a complete start - that's what I'd like to prevent - going thru the legalbabble screen again.
It would seem that I'd want to override onRestart in the second code fragment and put something in there to restart the app, right? That's the part I'm unclear about.
My question then is what needs to be done in onRestart. Do I have to recreate all the tabs and data in the tabs and all my object instantiations? Or is the memory state of the app saved someplace and then is restored back to the state that it was in before something else was brought to the foreground in which case not much needs to be done because all the objects and listeners will still be there?
Yes after the first activity has ended you shouldn't have to view that activity again. You could also write to the shared preferences that the user has previously seen legal info.
If you're UI object creation is in the onCreate method, this should only be called once. Pausing or resuming will not call the onCreate method again.
Unless you explicitly remove your objects and tabChangedListeners in the onPause method, you should not have to touch them in the onRestart method.
Correct, the state of the app is saved automatically. You shouldn't have to touch the onRestart method.
Hope this helps!
I think the problem is that the launch activity in your manifest is the legalbabble activity, so when you click on the icon, the system launches another one. A better architecture would be to launch the legalbabble activity it from your EtMain activity in the onCreate method of the latter, using startActivityForResult. From the docs:
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.
When you get the result in onActivityResult, you can call finish() if the legal stuff was declined; otherwise everything will proceed normally.
This avoids the problem that the launch activity defined in your manifest finishes when the legal stuff is accepted.
EtMain will not remain forever, if the user backs out (by pressing the BACK key) the Activity will be finished (onPause, then onStop, then onDestroy will be called).
In general you can ignore onRestore until you are doing something complicated.
Once the user has exited your application and re-enters (or presses the icon on the Homescreen), onCreate (followed by onStart and onResume) will be called for your first activity, so you do not need any logic in onRestart, your code in onCreate will do the setting up for you as it did the first time. Because of this your legal babble will appear again when the user starts the app after exiting unless you store a preference (in SharedPreferences or a database or file) to indicate you have already displayed it - in which case finish it straight away and start the main activity.
onRestart is only called when the application goes from the stopped state (onStop has been called but not onDestroy) to the started state (onStart is called but onResume has not yet).
For saving data - some components save their state automatically (e.g. EditTexts remember the text in them, TabHosts remember the currently selected tab etc). Some components will not. If you wish to save extra data then make use of onSaveInstanceState and onRestoreInstanceState. You should only use these methods to restore the state of your application or temporary data, not important things, e.g. the id of the resource what the user was looking at, what zoom level they were at etc. For things like contacts or actual data you should commit these changes to a database, SharedPreferences or other permanent storage (e.g. file) when onPause is called.
I recommend taking a look at the Android Activity lifecycle if you are confused. Or ask more questions!