I want to send a list of views inside a bundle, so the activity can be reconstructed using the values from before it get destroyed, for example when the user rotate the device, but I start getting errors on retrieving the views.. Is that okay to use that approach?
PS. I didn't post code because I think that would be unnecessary, if you guys think it would be nice to read the code, please request and I will update the question.
You seem to have the right idea but incorrect implementation. Use onSaveInstanceState, and if you want you can put things into your Bundle. Lets take a more practical example, such as a string value.
private TextView mTextView;
private static final String KEY_TEXT_VALUE = "keyTextValue";
#Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_TEXT_VALUE, mTextView.getText());
}
Whenever Android destroys and recreates your Activity for orientation change it calls onSaveInstanceState() before destroying and calls onCreate() after creating. So if you want, you can retrieve the saved information in onCreate() like this,
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextView = (TextView) findViewById(R.id.main);
if (savedInstanceState != null) {
String savedText = savedInstanceState.getString(KEY_TEXT_VALUE);
// do something with savedText
}
}
If you have a lot of data you may consider creating some sort of cache, SQLite would be sufficient enough.
It is ok to serialize the data the views need to redraw themselves and restore their state if the size of your list isn't really big. You should not be serializing the views itself.
When you serialize too much data, you will know as your app will crash :)
When there is too much data, use SQL instead or just tell your activity or fragment not to destroy itself and avoid this hassle altogether.
If you want to save the state of your Activity, save it into onSaveInstanceState(Bundle savedInstanceState) method, this method belongs to Activity class. For example:
savedInstanceState.putString(USER_NAME, myEditText.getText.toString());
In onCreate() method you can retrieve your data this way:
if (savedInstanceState != null) {
// restore as many values from views as you need
mUserName = savedInstanceState.getString(USER_NAME);
}
You may also consider using this line of code.
android:configChanges="orientation|keyboardHidden"
This will prevent your app from reseting your data when orientation changes.
Also see this tutorial.
Related
I want to have a global ArrayList variable in my android application and I was just wondering how to save this list of Fragments when the app is finished and to get that same ArrayList back when the app restarts?
Give an id to each Fragment and save the list of ids to SharedPreferences. When you restart app, inflate/display Fragments in the order of their ids that you saved to SharedPreferences.
How to write/read ArrayList to/from SharedPreferences: https://stackoverflow.com/a/22985657/5250273
You should just store the arraylist before your application is killed by android. And when your application is starting, your application should load the data.
Let me explain more.
Before this implementation, I think you should check the fragment's lifecycle.
onSaveInstaceState() method is called before onPause() (before fragment is killing). So you should save your data in this method.
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Storing my arraylist on bundle
outState.putStringArrayList("savedList", list);
}
To load your data, actually there is a OnRestoreInstanceState() method that is called after onCreate() ( before creating a new activity ) for activity. However there is no OnRestoreInstanceState() lifecycle method for fragments. So you can use onCreate(), onCreateView() or onActivityCreated() methods to load your date.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null){
// Loading my arraylist
list=savedInstanceState.getStringArrayList("savedList");
}
There are many options about storing data on android.
Saving key-value pairs of simple data types in a shared preferences
file
Saving arbitrary files in Android's file system
Using databases managed by SQLite
You should just make up your mind to choose your way to save your date.
I hope its help you.
I've imported some JSON data and fetched it into a custom arrayList. The arraylist looks like this:
ArrayList<HashMap<String, String>> postList;
It all works fine, but if the orientation changes the whole JSON parse/fetch process will start over again. That's why I like to store my custom ArrayList.
I have found a lot of information about (how to use) the Parcelable interface, but they all cover sending data from activity A to B.
Can somebody please provide an example about how to use a Parcelable inside the same activity? Thanks in advance!
You should search online for onSaveInstanceState, it's a callback which is defined inside Activity class.
It works in the same way when you need to pass data between activites
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
}
Inside outState you will use normal Bundle methods to pass Strings, Integers and Parcelable values (putParcelableArrayList/putParcelable/putParcelableArray).
When the screen rotate or something happens (activity got destroyed etc.), this method could be called and when the activity gets recreated the bundle you used here will be passed to onCreate inside the savedInstanceState argument (the only argument of onCreate, Bundle).
Here inside onCreate you check if (savedInstanceState != null) to be sure you have a saved state, if it's true you use savedInstanceState.getParcelableArrayList (or something else, depends on what you want to get) to read back the list.
if (savedInstanceState != null)
{
ArrayList<Parcelable> parcelableArrayList = savedInstanceState.getParcelableArrayList("key");
}
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putParcelableArray(myParcelableList);
}
It all works fine, but if the orientation changes the whole JSON parse/fetch process will start over again. That's why I like to store my custom ArrayList.
You mentioned that your orientation causes the whole process to restart in your Activity. Are you running the json... etc in your onCreate method? If that's the case, orientation changes cause onCreate to be called.
You can prevent onCreate from being called by modifying your AndroidManifest.xml to handle your own orientation changes for the current activity (look for the android:configChanges):
<activity
android:name="your.activity.package.andclass"
android:configChanges="orientation|screenSize">
<!-- etc -->
You might need to manually handle config changes for certain events, but I've never found it necessary.
Fragment losing state and shows an empty UI if left idle for 20 minutes. I'm using FragmentStatePagerAdapter and I have tried calling the notifyDataSetChanged in onStart() of my FragmentActivity.
Kindly help me how to save the object and state of my fragment and reuse it on reload of the app.
Android can kill your app if needed, you need to use onSaveInstanceState to keep your state in this cases. (Remember: Save important data in onPause!)
onSaveInstanceState exists in Activity and Fragments and is used in the same way like an activity
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("integer", anInteger);
}
Now in onCreate, onCreateView or onActivityCreated you have this argument BundlesavedInstanceState which corrisponds to the bundle saved. (Check if it's null too.)
If not enought maybe Android killed your FragmentManager too, so you need to override onSaveInstanceState and onRetoreInstanceState in your Activity and restore the fragment.
Maybe this answer could help you about the last thing i said: Using onSaveInstanceState with fragments in backstack?
A Fragment's life-cycle is closely tied to the Activity's lifecycle. This means, when your Activity goes idle; it will kill off any contained Fragments. To store Fragments you could always retain them in concordance with the Fragment API. This means you will generally be using the Fragment in a background. However the best way to keep a from being destroyed or lost from an Activity's end would be to store relevant information in a custom object and then to recreate the Fragment when the Activity is resumed.
For instance; I could have a custom object that would store relevent UI values for my Fragment and when my Activity either idles or changes I would save those relevant values to my custom object that I created. Then, when either a new Activity is created; or my old Activity is resumed; I would retrieve those values and put them back into my Fragment's UI. Hoped this helped :)
In case android needs memory, it kills the running apps. So you must save the objects using
public void onSaveInstanceState(Bundle savedState) {}
Note that savedState must be serializable.
You must call notifyDataSetChanged() in onResume(), because it ensures that it is called when the activity resumes.
For a detailed answer, please post your code.
Hard to answer without your code.
However I can say that the state is usually saved by the savedInstanceState
Usually in the onActivityCreated you have something like the following. In the example I give I save a boolean of either text was entered or not.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mHasText = savedInstanceState.getBoolean(HAS_TEXT_TAG);
} else {
// do something
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(HAS_TEXT_TAG, mHasText);
}
it's just an example as without your code it's difficult to anwer
Here is my set up.
I have a Main SherlockFragmentActivity. It swaps many ListFragments back and forth with FragmentTransaction's. To indicate loading, anytime a ListFragment loads the data I call:
setSupportProgressBarIndeterminateVisibility(true);
The problem:
When the main Activity mentioned above first starts, or the user leaves and goes to other apps and then restarts this one after an extended period of time, the SherlockFragmentActivity seemingly reloads, there is no progress dialog in the ActionBar, the screen is white for a few seconds, and then the list data repairs (The length depends on the data connection).
Here is some supplemental code: When the main/base Activity first loads, this is one of the first things I do in the onCreate():
// Set up Main Screen
FragmentTransaction t2 = this.getSupportFragmentManager().beginTransaction();
SherlockListFragment mainFrag = new FollowingFragment();
t2.replace(R.id.main_frag, mainFrag);
t2.commit();
FollowingFragment is the one that will always load in this instance. It contains a ListView and an AsyncTask pulling data from a MySQL database.
My question: How do I prevent this delay? And how do I handle maintaining the data when user leaves for extended periods of time?
This is the normal behavior, it happens because your activity has been killed to save memory for other apps, when your app was in the background. And when it comes back to the foreground, the system recreate your activity, which will recreate your fragment.
But if your really want to avoid recreating your fragment, you can use setRetainInstance in your fragment's onCreate method:
public void setRetainInstance (boolean retain)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
And use something like this in your FragmentActivity's onActivityCreated method:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getFragmentManager();
// Check to see if we have retained the worker fragment.
mRetainableFragment = (RetainedFragment)fm.findFragmentByTag("fragmentTag");
// If not retained (or first time running), we need to create it.
if (mRetainableFragment == null) {
mRetainableFragment = new RetainedFragment();
// Tell it who it is working with.
mRetainableFragment.setTargetFragment(this, 0);
fm.beginTransaction().add(mRetainableFragment, "fragmentTag").commit();
}
}
But be aware that, this should only be use for headless fragment (fragment without UI, i.e return null in onCreateView, aka worker fragment). You can still use this method for UI fragment though but it is not recommanded by google, in that case the data must be stored as member (field) in your activity. If the data which should be stored is supported by the Bundle class, you can use the onSaveInstanceState() method to place the data in the Bundle, and retrieve that data in the onActivityCreated() method.
Moreover this only works if the fragments is not added to the backstack.
According to the Android developer reference page on Activity, you have to request the progress bar feature before calling setSupportProgressBarIndeterminateVisibility():
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.activity_main);
setSupportProgressBarIndeterminateVisibility(true);
The other issue, reloading the fragments, is due to Android killing your ListFragment so that they have to reload could be resolved by overriding onSaveInstanceState(Bundle outState) and caching your data there to be retrieved in your ListFragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(savedInstanceState != null) {
// retrieve data from Bundle here
} else {
// no data, we need to reload from network
}
// initialize your View here
}
This method is not guaranteed to run all the time, however (it's not in the Fragment lifecycle). As such, you should also make sure you cache the data in onPause() and use it instead of always loading from a network connection.
#Override
public void onPause() {
super.onPause();
SharedPreferences prefs = getActivity().getSharedPreferences();
SharedPreferences.Editor editor = prefs.edit();
// put your data here using editor
editor.commit();
}
Then you can load this data in your onCreateView() by retrieving an instance of SharedPreferences and using prefs.getString(String key) and other methods.
When your app get killed, you lose your activity state and data! There are two scenarios that I can assume about your AsyncTask:
1. you are pulling some data from a Webserver. In this case I personally think caching your data which you retrieved from webserver is a better solution than implementing serializable.
2. You are pulling lots of data from local database (which causes retrieving data to take some time). In this scenario I suggest retrieving only as much data as you need, not more! (for example you can retrieve 20 items, and when user scrolling to the end of ListView retrieve next 20 items).
This solution helps your application retrieve data faster.
PS: To give you a clue how to implement the WebserviceModule with cache capability, which I assume is located in your AsyncTask, you can save every response from webserver in the SDCard and every time you trying to retrieve some resource from webserver, you should check the SDCard to see if your request already sent and cached! For every request, your should make a unique signature base on url and post parameters to recognize cached files.
When you return to activity after extending period of time, the whole app being restarted. So You can't rely on object variables to save data.
So You could avoid delay You've mentioned with saving data to some local storage in activity onStop() method. For example, shared preferences.
And when You call onCreate(), check whether You have data saved and use it if exists (and clean up to have "clean" start next time), otherwise start asynctask.
I have created several TextViews programmatically. When screen rotates I loose all added text views and their text values. What is the best way to save them and restore after rotation. I am using a tablelayout and adding rows each row has four textviews. I did not want to prevent device rotation.
You should use onSaveInstanceState to save your state, and then recreate it in onCreate. This works for both Activities and Fragments, but I believe the visibility on the methods is a bit different (they're public for Fragments).
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("text1", text1.getText().toString());
// do this for each of your text views
// You might consider using Bundle.putStringArray() instead
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize all your visual fields
if (savedInstanceState != null) {
text1.setText(savedInstanceState.getString("text1", ""));
// do this for each of your text views
}
}
Note that this is better than using onRetainNonConfigurationInstance; that method is used to actually keep objects around across rotations (e.g. Bitmaps), however you want to just store the strings, and thus using a Bundle is preferred. Also, onRetainNonConfigurationInstance isn't supported by Fragments; it's been replaced with setRetainInstance, and you don't want to use that for the same reason.
You need to persist the state across Device Rotation. As your view gets created dynamically, you need to store all this information in a 'Model' appropriate for your application. Android developer website has more info.
"To retain an object during a runtime configuration change:
Override the onRetainNonConfigurationInstance() method to return the object you would like to retain.
When your activity is created again, call getLastNonConfigurationInstance() to recover your object."
Take a look here: http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject