I am developing Android application and i have in my Main activity a fragment with this:
searchInnerPlaylistFragment = InnerPlaylistFragment.newInstance();
Bundle bdl = new Bundle();
bdl.putString(InnerPlaylistFragment.PLAYLIST_ID_KEY, ytf.getId());
bdl.putString(InnerPlaylistFragment.PLAYLIST_TITLE_KEY, ytf.getTitle());
bdl.putInt(InnerPlaylistFragment.PLAYLIST_VIDEO_NUMBER_KEY, videoNum);
searchInnerPlaylistFragment.setArguments(bdl);
ft.replace(R.id.search_inner_list_fragment, searchInnerPlaylistFragment).commit();
And when i open the "Don't keep activities" option in the developer Options, and go to background with my app and return to the app the searchInnerPlaylistFragment become null but it's still in the activity.
There is an option to save this fragment?
To correctly save instance state of Fragment, you should do following codes:
In the fragment, save instance state by override onSaveInstanceState and restore on onActivityCreated:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
if (savedInstanceState != null) {
//Restore the fragment's state here
}
}
...
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's state here
}
And important point, in the activity, you have to save fragment's instance on onSaveInstanceState and restore on onCreate.
public void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
//Restore the fragment's instance
mContent = getSupportFragmentManager().getFragment(
savedInstanceState, "mContent");
...
}
...
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "mContent", mContent);
}
Hope this help.
You can put your fragment on backStack, so that when you press back or navigate to back screen you can get it back from stack
change your fragment replacing line as
ft.replace(R.id.search_inner_list_fragment, searchInnerPlaylistFragment).addToBackStack().commit();
and when you want to get this fragment back you can pop it like
manager.popBackStack();
Related
I have an android application I am building and one surprising thing that happens is that it crashes when I turn off airplane mode while the application is in the foreground. I tried to include logs in the various lifecycle methods for the activity (onPause, onCreate, onResume) and for the fragment that's displayed the activity's onCreate. It so happens that all these call back methods are called in the following order:
onPause in the Displayed Fragment
onPause in the MainActivity
onCreate in the MainActivity
onViewCreated in the fragment that gets displayed in the MainActivity's onCreate
onStart in the Displayed Fragment
onStart in the MainActivity
onResume in the Displayed Fragment
and the app finally crashes in onPause in the displayed Fragment due to a NullPointerException.
I tried to trace all these callbacks and they are due to the fact that in the displayed fragment's onPause, there are some states that I save in SharedPreferences to restore them when the fragment is resumed and apparently they raise the exception.
May you please help me understand why an application goes through all these lifecycle methods when airplane mode is turned off while the app is in the foreground and the best way to prevent the app from crashing when this happens? I have gone through several posts concerning this issue and I have not found any answer.
Here is the code
public class MainActivity extends AppCompatActivity{
public static FragmentManager fragmentManager;
public static final String LOG_TAG = "LOG_TAG";
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(LOG_TAG, "Main activity created");
super.onCreate(savedInstanceState);
// init airplane mode receiver
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
if(isAirplaneModeOn){
Log.i(LOG_TAG, "Airplane mode turned on");// handle Airplane Mode on
} else {
Log.i(LOG_TAG, "Airplane mode turned off");
}
}
};
this.registerReceiver(receiver, intentFilter);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
displayFragment(new MapFragment());
}
#Override
protected void onPause() {
super.onPause();
Log.i(LOG_TAG, "Main activity paused");
}
#Override
protected void onResume() {
super.onResume();
Log.i(LOG_TAG, "Main activity resumed");
}
public void displayFragment(Fragment fragmentActivity){
// start the transaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentActivity).
addToBackStack(fragmentActivity.getClass().getSimpleName()).commit();
}
}
And the fragment
public class MapsFragment extends Fragment implements
OnMapReadyCallback {
public MapsFragment() {
// Required empty public constructor
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_map, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
FrameLayout map_frame = view.findViewById(R.id.map_frame);
// configure map
MapView mapFragment = view.findViewById(R.id.map_view);
mapFragment.onCreate(savedInstanceState);
mapFragment.onResume();
mapFragment.getMapAsync(this);
View mapView = mapFragment.getRootView();
super.onViewCreated(mapView, savedInstanceState);
}
#Override
public void onResume() {
super.onResume();
Log.i(LOG_TAG, "MapFragment resumed");
}
#Override
public void onPause() {
super.onPause();
Log.i(LOG_TAG, "Map fragment paused");
}
#Override
public void onMapReady(GoogleMap googleMap) {
Log.i(LOG_TAG, "onMapReadyCalled");
MapsInitializer.initialize(getActivity());
}
}
Thank you so much in advance.
I just realized that in my code, I was displaying the MapFragment in my MainActivity's onCreate which caused the app to crush. There is no way I could make the app not go through the lifecycle methods when turning off airplane mode while the app was in the foreground. However displaying the MapFragment in my MainActivity's onStart solved the problem.
#Override
protected void onStart() {
super.onStart();
if (getCurrentFragment() == null) {
displayFragment(new MapFragment(), null, false, true);
}
}
where getCurrentFragment() is a method I defined to return the fragment that is current displayed.
One quick question:
Why does my onSavedInstance not work? I want to save last state of user activity (current workout session etc.) but in some particular reason it keeps turning me back to the mainActivity when I press the home or overview button and then I return to the application. It should return me the last saved state of activity but something seems to be bugged. I have been struggling with this problem for two weeks. I searched all over the forum but I still can’t find the answer. Hope someone can help:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_workout);
if (savedInstanceState != null) {
// What should I call here?
} else {
// And here?
}
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
}
onSaveInstanceState(Bundle savedInstanceState) is called when the user leaves your app.It provides a Bundle object that you can pass values you want to save as key value pairs.
static final String WORKOUT_STATE = "state";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current workout state
savedInstanceState.putInt(WORKOUT_STATE, currentState);
super.onSaveInstanceState(savedInstanceState);
}
There are two options for where you can restore the current state when the activity is recreated. It can be done in onCreate(Bundle savedInstanceState) method or onRestoreInstanceState(Bundle savedInstanceState). If you should do it in onCreate, you should check if the savedInstanceState is not null.
If savedInstanceState is not null then the activity is being recreated and this is where you extract the values.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
currentState = savedInstanceState.getInt(WORKOUT_STATE);
} else {
// Initialize members with default values for a new instance
}
}
Instead of extracting the values in the onCreate method, you can optionally use the onRestoreInstanceState(Bundle savedInstanceState) method. This method is called if there is a state to restore so you don't need to check if savedInstanceState is null.
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore state
currentScore = savedInstanceState.getInt(WORKOUT_STATE);
}
You can read more about saving UI states here: https://developer.android.com/topic/libraries/architecture/saving-states.html
SOLVED!
I fixed my problem,
believe it or not the problem was in FLAG_ACTIVITY_NO_HISTORY in my intent service. Thanks for trying to help!
I am developing a very basic Android App that adds a number in TextView whenever I hit the Button. The digit showing in TextView is also preserved when the orientation of the Mobile changes using the onSaveInstanceState() and onRestoreInstanceState() functions.
The problem is when orientation changes the value is preserved but when the Button is pressed once again after the changing of the orientation it again starts the counting from 0 rather then starting it from the preserved value.
My code:
public class MainActivity extends AppCompatActivity {
TextView showValue;
int counter=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showValue = (TextView) findViewById(R.id.CounterValue);
}
public void countIN(View view)
{
counter++;
showValue.setText(Integer.toString(counter));
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("my_text", showValue.getText().toString());
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
showValue.setText(savedInstanceState.getString("my_text"));
}
}
Thank You for your response.
add
counter=Integer.parseInt(savedInstanceState.getString("my_text"));
inside onRestoreInstanceState method
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
showValue.setText(savedInstanceState.getString("my_text"));
counter=Integer.parseInt(savedInstanceState.getString("my_text"));
}
You can save and get data using several methods
First If your data is small, then you can use onSavedInstanceState and onRestoreInstanceState .. for details follow this answer or this one
Second if you have too much data to store, then use ViewModel instead; this is a tutorial that you can start from.
Hi I am practicing how Bundle savedInstanceState works in Activity creation and it restoration. I have tried this:
private EditText mTextBox;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextBox = (EditText) findViewById(R.id.etName);
mTextBox.setText("hello");
if(savedInstanceState != null){
Toast.makeText(this, savedInstanceState.getString("name"),
Toast.LENGTH_SHORT).show();
mTextBox.setText(savedInstanceState.geteString("name"));
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("name", "Joe");
super.onSaveInstanceState(outState);
}
On first onCreate() obviously this will set the EditText field with "hello" as savedInstanceState is null the if block will not be executed. When I change the orientation the Activity goes through all callbacks and Toast the String in the if block, however, it does not set the mTextBox with value from Bundle passed in. The EditText is still set to hello instead of Joe, however, the Toast in if block shows Joe.
Can anyone point out why this is not working as my expectation?
Thanks
This is happening as a result of TextView.getFreezesText, which will:
Return whether this text view is including its entire text contents in
frozen icicles. For EditText it always returns true.
And some more info from TextView.setFreezesText:
Control whether this text view saves its entire text contents when
freezing to an icicle, in addition to dynamic state such as cursor
position. By default this is false, not saving the text. Set to true
if the text in the text view is not being saved somewhere else in
persistent storage (such as in a content provider) so that if the view
is later thawed the user will not lose their data. For EditText it is
always enabled, regardless of the value of the attribute.
icicles is referring to savedInstanceState, that's just what it used to be called.
If you'd like to save and restore the text yourself, you could create a custom EditText and override getFreezesText, something like:
public class NonFreezingEditText extends AppCompatEditText {
public NonFreezingEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean getFreezesText() {
return false;
}
}
You could also use View.post:
mTextBox.post(() -> mTextBox.setText(savedInstanceState.getString("name")));
or Activity.onRestoreInstanceState
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mTextBox.setText(savedInstanceState.getString("name"));
}
You need setText different "hello". see example
private EditText mTextBox;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextBox = (EditText) findViewById(R.id.etName);
mTextBox.setText("hello");
if(savedInstanceState != null){
Toast.makeText(this, savedInstanceState.getString("name"),
Toast.LENGTH_SHORT).show();
mTextBox.setText(savedInstanceState.getString("name"));
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
mTextBox.setText("Joe");
outState.putString("name", mTextBox.getText().toString());
super.onSaveInstanceState(outState);
}
OR you must override onRestoreInstanceState. Not call onCreate when text not change.
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if(savedInstanceState != null){
mTextBox.setText(savedInstanceState.getString("name"));
}
}
This code always returns savedInstanceState as null
public class DemoidActivity extends Activity implements OnClickListener, android.view.View.OnClickListener {
EditText t1=null;
EditText t2=null;
EditText t3=null;
EditText t4=null;
String data1 = null,data2=null,data3=null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
t1=(EditText)findViewById(R.id.editText1);
t2=(EditText)findViewById(R.id.editText2);
t3=(EditText)findViewById(R.id.editText3);
t4=(EditText)findViewById(R.id.editText4);
Button b1=(Button)findViewById(R.id.button1);
Button b2=(Button)findViewById(R.id.button2);
b1.setOnClickListener(this);
b2.setOnClickListener(this);
}
Their is nothing wrong with the code as such. The reason why you are getting the savedInstanceState as null in your onCreate() method is because your activity is created for the first time and so there won't be any saved state as such.
It will only be set if you implement the method onSaveInstanceState().
http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)
It means that your activity's state is never saved.
Try to override the onSaveInstanceState() method:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Put your stuff in the bundle
}
Then use it to retrieve data/activity sate whether in the onCreate() method or in the onRestoreInstanceState() one:
if (savedInstanceState != null) {
//Retrieve data
}