This is not a question of how I can avoid my activity restart and recreation on say config changes lie e.t.c This is a question of why, despite all my efforts, my activity still restarts.What could be the possible reason?
Here is my scenario.
My app a text editing application, with tabs, so I implemented a custom TabHost and a custom TabWidget. The app has the MainActivity and a single fragment, MainFragment.
On To ensure instance is retained in my MainFragment, I have these:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
To ensure MainActivity instance is retained, I have this:
/**
* #see android.app.Activity#onCreate(android.os.Bundle)
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout frame = new FrameLayout(this);
frame.setId(android.R.id.content);
setContentView(frame, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
actionBar = getSupportActionBar();
if ((MyTextEditor.isSmallScreenSize(getResources()) || MyTextEditor.isNormalScreenSize(getResources()))
&& getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ) {
actionBar.setDisplayShowTitleEnabled(false);
}
if (savedInstanceState != null) {
editorFrag = (MyTedFragment) getSupportFragmentManager().getFragment(
savedInstanceState, "editor");
}
if (editorFrag == null) {
editorFrag = new MyTedFragment();
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, editorFrag).commit();
}
....
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, "editor", editorFrag);
}
And the following in my AndroidManifest.xml
<application
android:name="com.myapp.MyApp"
android:debuggable="true"
android:hardwareAccelerated="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/MyAppTheme" >
<!-- Ted main activity ... ie a text field :)
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustResize"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|fontScale|screenSize|smallestScreenSize">
-->
<activity
android:name="com.myapp.texteditor.MainActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|fontScale|screenSize|smallestScreenSize"
android:launchMode="singleTask" android:windowSoftInputMode="adjustResize">
</activity>
....
</application>
Now the problem:
CASE 1: Android version: 2.3.6 Gingerbread on Samsung device
If I push my activity to the background and I relaunch my app in less than about 3 minutes , the main activity instance is restored just fine.
If I choose to relaunch my app after a while longer, say 10 min or so,the app restarts afresh and this is bad for my app because if a user had any unsaved changes, they are lost.
CASE 2:
Android Version: 4.3, running on Emulator
How long I choose to wait before relaunching the app, the instance is always restored. Looks like everything works fine.
What could be the reason for this? Is this associated with a bug around the activity life cycle or Activity Manager for Gingerbread?
Your 1st case is something that you can't really avoid. A real device can not keep your activities around for forever (memory pressure etc). If your app is in the background then it's considered not important for the user (as he's not interacting with it) and it's a good target for the OS to kill if needed.
A perhaps useful option is SharedPreference.
This data will live until app is uninstalled but require a String key for each data entry, that it, is it not useful if the fragments are highly dynamic, though it often is possible to work around using arrays and the like.
Look at my answer to How to maintain fragment's state in the application for an example.
Related
Could the savedInstanceState parameter passed to Activity.onCreate have been saved by a different version of the app? I can't find any reference to this in the documentation.
This could be theoretically possible if the activity was backgrounded, then killed due to low memory, then a new version of the app was installed, and then the user navigated back to the activity using the recents list.
In the example code below, could onCreate throw an ArrayIndexOutOfBoundsException or set a surprising state if the enum has been changed between versions, or is the code safe?
public class ExampleActivity extends Activity {
private enum State {
SOMETHING,
SOMETHING_ELSE,
ANOTHER_THING,
ET_CETERA,
}
private State state;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
state = State.values()[savedInstanceState.getInt("state")];
} else {
state = State.ANOTHER_THING;
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("state", state.ordinal());
}
// ...
}
No. SavedInstanceState is not used to persist data across new instances of the same app. It is used to temporarily store data during the current app's 'session'.
In other words, the values you supply to the savedInstanceState bundle are stored when your activity is destroyed, for example, when you rotate the screen. These values are then retrieved when the activity is recreated.
When you quit the app entirely, or install a new version, these values are lost.
Theoretically always.
Before you upgrade (change) application. It deactivated and later starts over.
Now Android Studio added functionality "Instant Run", and in this case may possibly occur such an exception. More I do not have any ideas. It seems to me that such a check is not necessary right now. For all the time my development I've never encountered similar cases and have not heard about such an.
My application having landscape and portrait mode. so I designed two layouts for each mode. In manifest file I set the below code.
android:configChanges="orientation"
it's working fine but the activity restarts on every orientation changes. so I add the following line to avoid the recreate issue.
android:configChanges="keyboardHidden|orientation|screenSize"
Now the activity is not recreating but it also not taking the landscape mode design.
please help me to solve this issue friends
I would recommend letting the Application recreate itself on orientation change.
If you need to preserve state then store information when onSaveInstanceState is called.
/**
* On min or rotate save state info.
*
* #param bundle saved values
*/
public void onSaveInstanceState(Bundle bundle) {
bundle.putBoolean("SomeKey", someKeyValue);
super.onSaveInstanceState(bundle);
}
and then recover the values in onCreate
if (savedInstanceState != null) {
if (savedInstanceState.containsKey("SomeKey")) {
someKeyValue=
savedInstanceState.getBoolean("SomeKey");
}
}
Using onConfigChanges: is a slippery slope that works well until it doesn't.
Behavior varies and gets more complex with each OS release.
In my case my app work fine in both orientation here is what i doing may it help you,
use same code in public void onCreate(Bundle savedInstanceState) { // your code } and public void onConfigurationChanged(Configuration newConfig) { // your code }
and in AndroidManifest file
use like
<activity
android:name="YourActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden" />
that's it. if any issue post your comment
I'm having a problem with the Android Edittext.
I have a form. If the validation is not successful, I set an error to the edittext with the method setError().
However, on some devices, when I rotate the screen, the error disappears.
I have looked everywhere, but can't seem to find the solution to that.
Any ideas ?
When you rotate the device your activity gets Re-Created, The previous instance of activity gets destroyed and new instance of activity is created followed by all the methods of activity life cycle. Check on which method you are showing error in EditText.
Have you tried below codes in your manifest..
<activity
android:name=".YourActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" android:windowSoftInputMode="adjustPan"/>
Try adding android:configChanges="orientation|keyboardHidden" attribute for your activity in manifest.
I'm having the same problem. I thought android's view objects would handle this on their on, but they don't. So I think the solution is to save this information in onSaveInstanceState(Bundle) and restore it in onRestoreInstanceState(Bundle) for example (if you are working with a subclass of Activity). Here's an example to illustrate:
Say you are working with a subclass of Activity and you have a login form with two edit texts, one for email and one for password.
EditText mEmail = null;
EditText mPassword = null;
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
mEmail.setError( savedInstanceState.getString("email") );
mPassword.setError( savedInstanceState.getString("password") );
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("email", mEmail.getError());
outState.putString("password", mPassword.getError());
}
I am trying to implement orientation feature in the app. The app is basically made of tabs, using TabHost. Each tab is an activity group with a few activities. Each of this child activity runs a background task using AsyncTask to pull out server data and render the UI.
For instance,
HomeActivity- tab activity class that creates all the tabs
UserActivity- an activity group class representing a tab (say Tab 1)
TabOne- child activity inside UserActivity group
User sees Tab 1 soon after successful login. Issue begins when I tried to set orientation capabilities to this tab. Within the activity (TabOne), I added the following
#Override
public void onSaveInstanceState(Bundle outState) {
System.out.println("saving state..!");
outState.putStringArray("imageUrls", imageArray);
super.onSaveInstanceState(outState);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
System.out.println("restoring state..!");
imageArray = savedInstanceState.getStringArray("imageUrls");
super.onRestoreInstanceState(savedInstanceState);
}
I am able to obtain 'imageUrls' state. But, later down, I could not access the static tab host variable.
HomeActivity.tabHost.getTabWidget().getChildAt(1)
tabHost variable is null! I am not saving any of its state. How could I possibly save tabHost instance?
While researching other community posts, I found 'onRetainNonConfigurationInstance()'. I implemented the same.
public Object onRetainNonConfigurationInstance() {
System.out.println("retaining state..!");
return HomeActivity.tabHost;
}
Bu this object is returned as null. Can anyone walk me through steps to handle orientation changes in apps with tabHost?
Ask me if you find anything confusing.
Thanks in advance!
SOLVED: Adding screenSize to activity tag in the manifest fixed the issue.
<activity android:name=".HomeActivity"
android:configChanges="orientation|screenSize"/>
You can add the parameter
android:configChanges="orientation"
to your activity in manifest. I think it does what you want. It should look like:
<activity
android:name="com.your.package.YourActivity"
android:configChanges="orientation"
</activity>
Well basically, I press a button, this opens up your default camera app by using the camera intent. After a picture is taken, it will save the things needed and redirect to another activity.
In this activity, I have an AsyncTask that can succesfully upload pictures. So what is my problem you may ask. My problem is that it re-creates my activity and therefore reset my ProgressDialog together with it. ( It runs the activity, does the aSyncTask, dies before it can finish it and re-creates my Activity to do the asynctask once again. )
It does not always do this. I think it does this because it changes the Orientation from the phone from Landscape to Portrait. ( I have a Samsung. When I go to the Camera it changes to landscape and when I finish it, it goes back to portrait. )
I've already done my homework and added these things to my manifest:
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait" >
I've made sure to "lock" my app in the portrait orientation but I still see my app change orientation and I believe this is why my activity gets re-created.
I was planning to add all kinds of checks but I believe this is not the right way to handle this situation, since it sometimes does not re-create the activity.
The check I am talking about is to use:
protected void onSaveInstanceState(Bundle outState) {
outState.putString("started", "1");
}
Anyway, can somebody help me out? I just want it to load the activity without it self-destructing on me.
PS: The VM doesn't have any problems. The VM loads the activity and finishes it without re-creating it.
PPS: Did extra testing, on my Samsung if I keep it on landscape-mode it will work. So it is definately the camera that is destroying my activity with it's orientation change.
I had the same issue, turns out you also need to listen for screen size changes in API level 13 or higher as explained here; https://stackoverflow.com/a/11483806
android:configChanges="orientation|screenSize"
For this to fix, I had to use following in my manifest file:
android:screenOrientation="portrait"
android:launchMode="singleTop"
android:configChanges="keyboardHidden|orientation|screenSize"
Try creating a fragment activity to handle displaying and updating the progress dialog
In the fragment activity make sure and set "setRetainInstance(true);" This will make sure it isn't destroyed when the main activity gets created/destroyed.
It's probably a good idea to put the entire image capture process inside this fragment, including the asynctask. Make sure you don't reference the parent activity's context from within the doInBackground() in the AsyncTask. If you do this and the orientation changes (i.e. the activity is destroyed) it will throw an error.
here's a rough example:
public class MyFragment extends FragmentActivity {
private ProgressBar mProgressBar;
private boolean mAsyncTaskActive = false;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
// grab reference to progress bar
mProgressBar = (ProgressBar) getActivity().findViewById(R.id.my_progress_bar);
// check to see if the async task is active and set the progress bar visibility accordingly
if (mAsyncTaskActive) {
mProgressBar.setVisibility(View.VISIBLE);
mProgressBarText.setVisibility(View.VISIBLE);
}
}
// this method is called from your main activity when the user does something (i.e. clicks a button)
// make sure you have already instantiated the fragment
public void startSomething() {
if (mAsyncTaskActive == false) {
mProgressBar.setVisibility(View.VISIBLE);
new MyAsyncTask().execute();
mAsyncTaskActive = true;
}
}
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
Context applicationContext;
#Override
protected Void doInBackground(String... params) {
// do stuff
publishProgress(//some number);
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
}
You should also take a look at how to implement fragments if you're not already familiar. The Android dev blog has a good post on DialogFragments, same priniciples. http://android-developers.blogspot.com/2012/05/using-dialogfragments.html