I was thinking. What is the best way to save a custom adapter before activity onDestroy() is called? I want to populate an adapter with items (texts and images) and set it to listView. However, I don't want to repopulate the adapter again when the user navigate away from that activity and comes back as repopulation is too time consuming. I want to save the adapter value somewhere before the activity inDestroy() is called and check if it empty on activity onCreate.
Well, adapter is a pretty complex object and its persistent saving may be a difficult task (if possible at all). The more common approach is saving persistently your dataset.
You worry about the population time, but serialization-deserialization of the adapter is going to take time as well, and apparently much more time then the dataset alone, because it includes the dataset in it.
EDIT
Small conceptual example on saving your dataset to SharedPreferences using Gson library (more on it) (just one of the ways to persistently save your data):
public void saveData(ArrayList<YourDataType> data) {
Gson gson = new Gson();
String dataJson = gson.toGson(data);
getSharedPreferences("your_prefs", Context.MODE_PRIVATE).edit()
.putString("key_data", dataJson)
.apply();
}
public ArrayList<YourDataType> restoreData() {
String dataJson =
getSharedPreferences("your_prefs", Context.MODE_PRIVATE)
.getString("key_data", "empty");
ArrayList<YourDataType> data = null;
if (!dataJson.equals("empty")) {
Gson gson = new Gson();
Type collectionType = new TypeToken<ArrayList<YourDataType>>() {}.getType();
data = gson.fromJson(dataJson, collectionType);
}
return data;
}
You can save adapter data inside application class and when activity is recreated check Application class arraylist is empty or not. If not empty
assign it to adapter.
But storing it inside global variable i.e Application class may make your app heavy on heap memory.
public class GlobalState extends Application {
ArraList<Type> arrayList = new ArrayList<type>();
public void setArraylist(ArraList<Type> arrayList) {
this.arrayList = arrayList;
}
public ArraList<Type> getArrayList() {
return arrayList;
}
public int dataSize() {
return arrayList.size();
}
}
Related
Is it a good idea to pass a list of object from one Activity to another for Android ?
It is quiet troublesome to pass a list of object in an intent, and I wonder whether it affect the performance if the object list is too large or the object is too complicated.
Is it a better solution to get the list of object from other place, for example, query the DB once more , or save the list of object in a temporary class and fetch it in new Activity?
As long as you are passing Parcelable objects' list, nothing's wrong when passing through Intent. Regarding performance, that is up to you and the amount of data you pass.
As per my experience, If you are passing data up to 1MB of size, it should be fine. Anything above that will fail as this seems to be the limit. Read this.
Besides, you are welcome to use preferences, SQLite, files or static-object-referencing methodologies to fetch your data anytime and anywhere.
Solution1 : Use Intent
Send :
Intent data = new Intent(Activity1.this,Activity2.class);
data.putParcelableArrayListExtra(Constant.LIST_OBJECT,
(ArrayList<? extends Parcelable>) getObjects());
receive :
List<YOUR_OBJECT> objects = data.getParcelableArrayListExtra(Constant.LIST_OBJECT);
Solution2 :
public class SessionData {
private static SessionData instance;
private final List< YOUR_OBJECT > listSessionObjects;
private SessionData() {
listSessionObjects = new ArrayList<>();
}
public static final SessionData getInstance() {
if (instance == null) {
instance = new SessionData();
}
return instance;
}
public List<YOUR_OBJECT> getListSessionObjects() {
return listSessionObjects;
}
public void setListSessionObjects(List<YOUR_OBJECT > objects) {
listSessionObjects = objects
}
}
to use it :
SessionData.getInstance().getListSessionObjects();
SessionData.getInstance(). setListSessionObjects(objects);
I'm currently using a custom list adapter and modifying the rows of the listview at runtime (changing text, buttons etc).
I want to find a way to save/restore the changes made to the listview at runtime when I start another fragment and then come back to the fragment containing the listview.
Currently the listview is being reloaded every time and all the runtime changes are lost.
The following shows how I am setting the list adapter.
public void setAdapterToListView(ArrayList<item> list) {
ItemList = list;
ItemAdapter adapter = new MenuItemAdapter(list, getActivity(), this);
ItemListView.setAdapter(adapter);
}
I am then using a custom list adapter to make the changes (mentioned above) at runtime. Here's a snippet of how I am changing things at runtime:
if (holder.qty.getText().toString().equals("Qty")) {
relativeLayout.setBackgroundColor(row.getResources().getColor(R.color.navDrawerL ightBlue));
holder.qty.setText("1");
holder.qty.startAnimation(anim);
holder.removeItem.setBackgroundResource(R.drawable.remove_item_red);
holder.removeItem.setEnabled(true);
...
Is there a recommended way to approach this issue?
Adapter
You will want your custom adapter to implement some sort of getter for it's internal data. For instance
public ArrayList<YourDataType> getList() {
return new ArrayList<YourDataType>(mAdapterData);
}
Activity
Then in your activity/fragment, you'll need to save and restore that data.
private static final String STATE_LIST = "State Adapter Data"
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList(STATE_LIST, getAdapter().getList());
}
While there is an onRestoreInstanceState() method you could override, I typically restore during onCreate(). Usually more convenient to when other things are getting instantiated. Either or is viable.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//If restoring from state, load the list from the bundle
if (savedInstanceState != null) {
ArrayList<YourDataType> list = savedInstanceState.getParcelableArrayList(STATE_LIST);
ItemAdapter adapter = new MenuItemAdapter(list, getActivity(), this);
} else {
//Else we are creating our Activity from scratch, pull list from where ever you initially get it from
ArrayList<YourDataType> list = getInitData();
ItemAdapter adapter = new MenuItemAdapter(list, getActivity(), this);
}
}
YourDataType
You didn't mention what YourDataType was but I'm assuming it's a custom class. In order to work with the bundled savedstate, it must implement Parcelable. Android's dev link on Parcelable and a StackOverFlow post explaining how to write your own custom class with Parcelable.
Update
Depending on what you are doing with the fragment will dictate if the onSavedInstanceState() method will be called. From the way your question is asked, I'm assuming you are pushing one fragment onto the backstack to load another on top. Then hitting the back button will reloaded that fragment from the backstack...and pass along the bundled state to reload from.
Of course that is just one of many scenarios. It's perfectly possible for the fragment in question to only perform onPause() followed by onStop()...then when redisplaying the fragment, only seeing it do onStart() followed by onResume(). In this case, there would not be a saved state because the fragment was never fully destroyed, so there is nothing to restore. It should be in the same state. If it's not and instead you are seeing it reload the initial data...then you are probably reloading the initial data in or after onStart(). You'll want to move all that initializing data to onCreate() instead.
Another possible case is that you completely destroy the fragment without ever putting it on the backstack. Re-initializing it would not have a saved state to pull from. If you wanted to "restore" this fragment back to the state beforehand, then you can not rely upon onSavedInstanceState(). You will need to persist that information manually somewhere in memory, to disk, or a DB yourself. Then pull from it accordingly.
Life cycles with fragments are unfortunately really complex and depend greatly on usage and even between the support library vs native fragments.
After doing some reading/research I ended up solving this by saving the adapter data to Saved Preferences by using Gson (https://code.google.com/p/google-gson/).
I used Gson to first convert my array of objects into Json and then stored the Json strings in the Saved Preferences.
Subsequently I was able to retrieve these as required and read the Json into Java objects again, store these in an array list and pass this to my listview adapter.
Here's a brief overview of the process for anyone who is looking to do something similar.
Saving to Shared Preferences:
SharedPreferences settings;
Editor editor;
settings = c.getSharedPreferences(PREFS_NAME,Context.MODE_PRIVATE);
editor = settings.edit();
Gson gson = new Gson();
String ItemsJson = gson.toJson(list);
editor.putString(categoryId, ItemsJson);
editor.apply();
Reading from Shared Preferences:
if (settings.contains(categoryId) {
String jsonItems = settings.getString(categoryId, null);
Gson gson = new Gson();
Item[] favoriteItems = gson.fromJson(jsonItems, Item[].class);
list = Arrays.asList(favoriteItems);
ItemsFromSharedPrefs = new ArrayList<>(list);
AllCategories.addAll(ItemsFromSharedPrefs);
} etc...
In my application whenever we click the "SEARCH" button it makes Http call and get the json as response. I parsed the json and save the data in arraylist bdata.
ArrayList bdata = new ArrayList();
BusData contain getters and setters . I also used one adapter class for custom listview.
now i want to set the adapter to listview but the listview is in another class (not in the class where search button is locate) so how to send my arraylist from one activity to another activity with the help of intents for set the adapter to listview.
Thank you.
A way to do this is by serializing your arraylist to a JSON.
Use GSON library
compile 'com.google.code.gson:gson:2.3.1'
Add the above line in your build.gradle file.
Make a class to help you serializing your objects
public class SerializationHelper {
private static Gson gson = new Gson();
public static String serialize(Object object) {
return gson.toJson(object);
}
public static Object deserialize(String json, Class classType) {
return gson.fromJson(json, classType);
}
}
Now when you want to start the new activity add the serialized string.
String json=SerializationHelper.serialize(myArrayList);
intent.putExtra("data",json);
And in your new activity on create get it and create your object again.
String json=intent.getStringExtra("data");
Object deserializedObject=SerializationHelper.deserialize(json,ArrayList.class);
Now cast your object!
ArrayList<MyClass> myCoolArray=(ArrayList<MyClass>)deserializedObject.
Other simpler way is to make your arraylist static and public, and store it in an other class.
public class GlobalStuff{
public static ArrayList<MyClass> myAwesomeList;
}
Now acces your list by GlobalStuff.myAwesomeList.
You need to create custom class (ArrayList<data class>) to Parcelable and then you can pass it to other activity.
http://androidideasblog.blogspot.in/2010/02/passing-list-of-objects-between.html
When passing data through activities you should use Parcelable, the are many examples in stackoverflow, this one is good:
https://stackoverflow.com/a/22446641/2091315
Next, you need to configure the intent, on the "sender" Activity:
Intent intent = new Intent(getApplicationContext(),
ReceiverActivity.class);
intent.putExtra("arrayListIdentifier",parcelableArrayClass);
startActivity(intent);
On the "receiver" Activity:
parcelableArrayClass myParcelableObject = (parcelableArrayClass) getIntent().getParcelableExtra("arrayListIdentifier");
I have a ListView containing news from RSS feeds in MainActivity, the problem is I have to stream the RSS feeds everytime I open the app because the items of the ListView are destroyed when I close the app.
I understand I can save it in SQLite temporarily, but is there a more simple way to save the ListView layout just so it would still be there next time I open the app?
Another option is to use SharePreferences and Gson to convert your datasource for the list view into a string for storage and then when the app is re-opened you can rebuild the list view with the stored data fairly quickly. I do something similar in one app where the data source for my list view is a ArrayList of LinkedHashMap items, so this would be the two methods for converting the ArrayList to a String and then back to an ArrayList when needed
public static String ArrayListToString(ArrayList<LinkedHashMap> list) {
return gson.toJson(list);
}
public static ArrayList<LinkedHashMap> StringToArrayList(String input) {
Type collectiontype = new TypeToken<ArrayList<LinkedHashMap>>(){}.getType();
ArrayList<LinkedHashMap> list = gson.fromJson(input, collectiontype );
return list;
}
I would also then suggest storing a timestamp so you can check if the stored list should be displayed or if an updated list needs to be retrieved
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.clientListView)
...whatever other setup you want to do here
CheckTimeStamp();
}
public static void CheckTimeStamp() {
String timeStamp = preferences.getString("keyClientTimeStamp", "");
Calendar calendar = Calendar.getInstance();
Date date = calendar.getTime();
String currentTime = HelperClass.GetSimpleDateFormat(date);
if (currentTime.equals(timeStamp)) {
String storedString = preferences.getString("keyStoredClients", "");
clientArrayList = HelperClass.StringToArrayList(decryptedArray);
//the setupView method is where I take my ArrayList
//and add it to my ListView's Adapter
SetupView();
}
else {
SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.keySalesTimeStamp, currentTime);
editor.apply();
//for me this is a web service that get's a list of clients,
//converts that list to a String to store in SharedPreferences
//and then calls SetupView() to add the list to the ListView Adapter
GetClientList();
}
}
Hmm, I would use Volley library for caching requests, I guess it very simple: you do request and next time first of all get it from cache. You don't have to explicitly save and describe a model for storing data for this case.
Below, I gave the example of how it might look:
public class RSSProvider {
private final RequestQueue mRequestQueue;
// ...
private RSSProvider(Context context) {
mRequestQueue = Volley.newRequestQueue(context.getApplicationContext());
}
// ...
public void getSomething(final Response.Listener<String> responseListener, final Response.ErrorListener errorListener) {
if (mRequestQueue.getCache().get(<URL>) != null) {
responseListener.onResponse(new String(mRequestQueue.getCache().get(<URL>).data));
} else {
StringRequest request = new StringRequest(Request.Method.GET, <URL>, responseListener, errorListener);
request.setShouldCache(true);
mRequestQueue.add(request);
}
}
// ...
}
My entity contains the following private ForeignCollection attribute:
#ForeignCollectionField
private ForeignCollection<Order> orderCollection;
private List<Order> orderList;
What is the best way or usual way to avoid a having a caller use a ForeignCollection? Is there any neat way to return the Collections data to a caller?
How does the following method look? It allows a caller to access the data via a List. Would you recommend doing it this way?
public List<Order> getOrders() {
if (orderList == null) {
orderList = new ArrayList<Order>();
for (Order order : orderCollection) {
orderList.add(order);
}
}
return orderList;
}
If it's ok to change the signature to Collection rather than List, you could try using Collections.unmodifiableCollection().
public Collection<Order> getOrders()
{
return Collections.unmodifiableCollection(orderCollection);
}
Otherwise, your approach of using a lazy member variable is fine (provided you don't need synchronization). Also, note that you can just use the constructor of ArrayList to copy the values from the source collection:
orderList = new ArrayList<Order>(orderCollection);