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.
Related
I am creating a simple survey app in android studio. I have multiple intents, and I start each one when the "next" buttons are pressed. I also have a back button that brings the user to the previous intent if they want to change an answer to a question. When I go forward again, none of the answers are saved. How can I get all the users answers to be maintained?
There are two methods you can override in your activities:
public void onSaveInstanceState(Bundle savedInstanceState);
public void onRestoreInstanceState(Bundle savedInstanceState);
As you see you have access to the Bundle "savedInstanceState". If a user terminates an activity onSaveInstanceState() gets called and you have the possibility to save data which you want to restore later.
The method onRestoreInstanceState() only gets called if you have saved information in onSaveInstanceState() and offers you the chance to restore that data. It provides a bundle which contains the same data that you saved into the data during onSaveInstanceState().
A bundle is basically a key-value list. To store a string for example you simply would call the putString()-Method of the bundle:
savedInstanceState.putString("myKey", "Hello World");
There are many data-types that you can store in a bundle (in general, every class that implements the interface Parcelable can be stored in a bundle).
More information:
http://developer.android.com/training/basics/activity-lifecycle/recreating.html
http://developer.android.com/reference/android/os/Bundle.html
How about using a singleton class for your data and store the intends there in a List or Array. You can extend the Application class for this or just make your own singleton class.
When your Activities are recreated they receive the callback onCreate(Bundle). Whatever you have saved in onSaveInstanceState(Bundle) will appear in Bundle.
You can implement a singleton class ,and use it in your application which extends Application.
Store your data to the singleton by map and provide getter and setter methods
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.
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.
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.
So I would like to save an arrayList of strings in onPause and get it back in onResume but the problem is I can't use sharedPreferences because my API level is 8 and I can't use the putStringSet function. Also, I would like to avoid saving data to the user's phone via a file system. I'm already doing that with several other things.
Is there any other way to save the data? I've tried putting a bundle in onPause and getting it in onResume and that hasn't worked, I've also tried implementing onSavedInstanceState and onRestoreInstanceState but those only get called when the activity is destroyed and I'm only putting it on pause most of the time.
make that arraylist static or create a singleton class for that
class data
{
public static ArrayList aa;
}
now on pause function
onPause()
{
data.aa=
}
this way you can have it maintained
You can use Modified Shared Preference to store your ArrayList. I was trying similar and I got answer for that. Here is the link. CustomSharedPreference
Here You can create one class with getter and setter for ArrayList and that class as a object in Shared Preference.