In my app (which is a game), I have an 'Enemy' class, for example like so:
public class Enemy extends Sprite implements Serializable {
public Enemy(EnemyType type){
super();
}
}
I have then declared an ArrayList like so:
ArrayList<Enemy> enemyList = new ArrayList<Enemy>();
To which I can add enemies:
enemyList.add(bird);
enemyList.add(bee);
When saving to the Bundle I simply put:
bundle.putSerializable("Enemies", enemyList);
And when restoring from the Bundle, I have this:
enemyList = (ArrayList<Enemy>) savedState.getSerializable("Enemies");
Now, it does seem to restore the arraylist (I can check it's size and it is always correct - ie, the same size on restoring from the bundle, as it was when saving to the bundle.
I have also logged for example, the first index of the ArrayList and sure enough it lists the enemy instance as being there.
However, if I try to manipulate the ArrayList at any time post-restoration, I get an exception telling me that I'm trying to perform [whatever action] on a Null object (enemyList).
If I simply populate the list myself, so have something like:
enemyList = (ArrayList<Enemy>) savedState.getSerializable("Enemies");
enemyList.add(bird);
enemyList.add(bee);
Then everything works as expected.
I'm assuming this has something to do with the fact that the super class of Enemy isn't serialised? However, if I serialise this, I get a 'notSerializableException' error.
Please note, I'm not really too worried about saving/restoring the actual Enemy objects to the Bundle, I can handle this manually. Rather I just want the list to be in the same state as it was. And I thought that what was stored in the ArrayList were just references to the objects in question, therefore I can't work out why this is happening?
Any ideas what I'm doing wrong or is there a better method to achieve that which I'm trying to achieve?
The recommended way of doing this in Android is to make the objects you want to persist to be Parcelable. This is a type of serialization specific to Android.
Have a look at the official documentation here
Related
I have a custom class with 3 String fields (arg1, arg2, arg3) and getters/setters. In Fragment there are 3 editText fields that create a new Object of custom class. And I also have an ArrayList of custom objects (max capacity is 15, but its usualy around 1-3) that is stored in SharedPreferences.
Everything worked fine until last update in which I only changed some UI design features in xml. Not a single line of Java code was changed, especialy those that have something to do with Custom objects, ArrayList or SharedPrefs.
Now some users, after updating my app, get corrupted first (or in most cases, only) entry of ArrayList, and arg1 is "" or null.
The worst thing is they are displayed the value of arg2 where is explicitly called textView.setText(arg1).
Im saving my SharedPrefs in onStop() and restoring it in onCreate() and I'm using Gson/Json for storing. In 99,9% everything is fine, but after recent update, some users reported this problem.
What's going on, and how to prevent it from happening in the future?
EDIT:
What about the fact that I set my ArrayList to be public static, so I can use it elsewhere, would it be a problem? Also since I can't debug production app, I've noticed that either args or their values have moved like MyCustomObject("val1", "val2", "val3") is now MyCustomObject("val2", "val3", "???") I dont know what last value is. Is it val1 or ""
Everything worked fine until last update in which I only changed some
UI design features in xml.
UI classes (like your custom UI class with 3 EditText) should not be serialized, because UI classes tend to change over time and it is not the pattern. You should only store/serialize data/object that represents UI states, not the UI objects themselves.
Im saving my SharedPrefs in onStop() and restoring it in onCreate()
and I'm using Gson/Json for storing. In 99,9% everything is fine, but
after recent update, some users reported this problem.
Suppose, the newer version of a UI class includes an extra member variable. Now, serializing in the newer version will give a different JSON string than the previous version. And as a result, your UI logic might conflict.
Workaround
In this case, you should make ArrayList of the custom class with 3 String fields (arg1, arg2, arg3) and getters/setters, then serialize/deserialize this ArrayList with Gson or any other library. Note that, the following example is basic and not perfect since no associated code provided.
In onCreate(), initialize the UI components with this data. For example:
// Retrieve data and initialize custom data holder by deserializing stored json
CustomObj customObj = gson.get(typeToken, str);
// Use this data to initialize UI components
customUiObj.editText1.setText(customObj.arg1);
customUiObj.editText2.setText(customObj.arg2);
customUiObj.editText3.setText(customObj.arg3);
In onStop(), retrieve data from the UI components to store. For example:
String arg1 = customUiObj.editText1.getText().toString();
String arg2 = customUiObj.editText2.getText().toString();
String arg3 = customUiObj.editText3.getText().toString();
// Now, create a data holder object
CustomObj obj = new CustomObj(arg1, arg2, arg3);
// Serialize this obj
String jsonStr = gson.toJson(customObj);
So, I am running up against a limitation of the Parse SDK here, and I don't know how to proceed.
I have a ListView that I am setting the adapter to from a subclass of the ParseQueryAdapter, which itself extends the BaseAdapter class.
Normally with an adapter, I pass the list along to the Adapter constructor in order to get access to the list I am dealing with, so that I can call getCount, getItem, etc. by accessing the size/index of that ArrayList.
I can't do that with the ParseQueryAdapter because it takes a ParseQueryFactory as part of the constructor, and it doesn't resolve it's items until they are fetched from the Parse app online...
Now the ParseQueryAdapter does have a List of Objects, which is exactly what I need, expect that it is Private... so even though the getItem() and getCount() methods exist, there is no way - AFAIK - to use them, as I have no access to the dataset that is fuelling my adapter...
Further to this, I would normally use getView(), which returns a position alongside my View, and everything would be snappy. Except Parse overrides that and provides a getItemView() method which resolves the Parse Object that is fetched from the cloud and passes it instead of the integer for position...
So, two questions here:
1) Is there another way in which I can access the list that is feeding an adapter ? I can hack a way to pass the resolved query into a list and pass that into the adapter later, by listening to addOnQueryLoadListener(), but that seems bad design and might take a while, handcuffing my UI.
2) Am I being naive in my understanding of how to use get getItem() and getCount() methods ?
I have a LinkedHashMap that I want to pass through the Bundle savedInstanceBundle to store it between screen rotations. How do I do this safely? Before I just cast it because I know what I'm putting into it and what I'm getting out of it, but this did generate a warning that it was considered an unsafe cast.
What I am doing now :
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
if(thumbnails != null) {
savedInstanceState.putSerializable("thumbnails", thumbnails);
}
}
and retrieving it
thumbnails = (LinkedHashMap<Long, Bitmap>)savedInstanceState.getSerializable("thumbnails");
What is the correct way to deserialize LinkedHashMaps from a Bundle? Can it be done at all? If not, how do I preserve order without adding another layer in between that keeps track of the position which would involve changing a lot of existing code?
` Caused by: java.lang.ClassCastException: java.util.HashMap cannot be cast to java.util.LinkedHashMap`
From the MVC (Model -- View -- Controller) point of view, an Activity is a Controller. (And so is a Fragment.) The View is made from the XML and the Android View subclasses that you reuse (people rarely define custom View subclasses). And the Model is --- well, you have to define a class for the Model yourself! If you had Model, you'd look from a different perspective.
But if you nevertheless want to pass data from one Activity incarnation to another -- well, why don't you use a JSONObject/JSONArray? It's an overkill, it will be slow, but it should at least work.
Another possibility is to convert the LinkedHashMap into a list of key-value pairs and later reconstruct it from that list.
Basically, I need to add items to the arraylist. Am I correct in using a separate class (this is in part what I am practicing here) or is that a stupid idea because it is basically just an arraylist. I thought I would create a class because I want to randomise additions to it, etc. Alternately, since it is just an arraylist class, should I use "extends arraylist" or something similar??? Code examples most welcome. I am obviously missing some crucial programming theory here. Thank you.
NOTE I have removed what I think is unrelated code
I have a main:
public class cgame2 extends Activity {
private ArrayList<Integer> cToChooseFromImages; // clear image list
colourlist colourlistused = new colourlist();
colourlist userscolourlist = new colourlist();
....truncated....towards the end of my code I have......
colourlistused.randomaddnewitem();
And I have a class (called colourlist):
import java.util.ArrayList;
import java.util.Random;
import android.util.Log;
public class colourlist {
private ArrayList<Integer> clist;
private int picked;
int imagetodisp;
public colourlist() {
}
public void randomaddnewitem() {
Random randomtouse = new Random(System.nanoTime());
picked = randomtouse.nextInt(3);
clist.add(picked); // this is where the program crashes according to log info
}
public void addnewitem(int itemtoadd) {
clist.add(itemtoadd);
}
.....etc.....
If you're adding functionality to ArrayList, the best way is just the way you have done it.
Create a member ArrayList of your class and create functions to extend the behavior. This is called Composition.
Contrast with Inheritance, or creating a subclass of ArrayList, which can create more problems by unnecessarily coupling your implementation to ArrayList.
Don't forget to initialize the list. The best way would be:
List<Integer> clist = new ArrayList<Integer>();
Use the interface List to make your code more flexible if you choose to change the specific list implementation later.
Creating a class for your array list isn't a bad idea, classes in and of themselves are data structures, and this will allow to manipulate the data easier. By having the array list in it's own class, you will be able to get it, set it, send it, alter it, etc, with ease. However, when it comes to creating classes, you generally want to create classes that could be considered objects. If you are a beginning programmer, I would recommend exploring and researching object oriented analysis and design, as that will help you determine what should and shouldn't be classes.
And, as others have pointed out, make sure you initialize your array list:
List<Integer> clist = new ArrayList<Integer>();
As for extending ArrayList vs. List, I would use ArrayList because 1. It has all the methods and functionality of List (because is is an implementation of List) and 2. It is backed by an array, which is what you are using. Just my preference, feel free to do some research and figure out what is going to work best for what you need; a List may be all you need, in which case, you should use that.
One other things I will point out, since it looks like you are creating a game, is that if you are going to pass and object, in your case and ArrayList, you are going to want to get familiar with the Parcelable interface. It is what will allow you to pass data from one activity to another. Here is a link that will get you started, along with a tutorial that might offer some insight to what is needed:
Tutorial for Parceleable:
http://shri.blog.kraya.co.uk/2010/04/26/android-parcel-data-to-pass-between-activities-using-parcelable-classes/
Developer Info:
http://developer.android.com/reference/android/os/Parcelable.html
Best of luck!
I use a custom list adapter and ArrayList for my ListView. This solution was good enough but now I need to use Map of ArrayLists, something like this:
TreeMap<String, ArrayList<ItemsModel>>
where ItemsModel is a Java bean. Earlier I used to populate this ArrayList it that way:
itemsDataArrayList.add(itemModel)
Now I faced some difficulties with Map interface. First, I don't know how to populate my new Map structure: I suppose this
mapInstance.put(itemModel.getItemName.toString(), itemsDataArrayList)
won't work because itemsDataArrayList is the list of elements, not a certain element.
Second, I'm not sure how to properly declare this map instance in my ItemsAdapter class. When I was using just ArrayList it was very simple. Some examples would be very helpful.
What I recommend you look into / try is creating your own BaseAdapter. When you override this class it will give you all the functions you need to override to populate the list view.
This has the advantage of giving you complete control of what is put into the listview and how each view is created.
Then after you get it working I recommend looking into the ViewHolder design pattern along with recycling views which is a great way of improving efficiency when scrolling the listview.
What you are really looking for seem to be a MultiMap. To your first Question - your attemp was quite good, you can only put ArrayLists as values into your TreeMap.
The Problem with this might be, that if you want to add some ItemsModel to your Map, you first need to get the List of the key, and then add the ItemsModel to that list. Additionally you need to ensure, that this list for this particular key exist, and if not, create it.
Example:
String key = "hi";
ArrayList keyList = mapInstance.get(key);
if (keyList == null) {
keyList = new ArrayList();
mapInstance.put(key, keyList);
}
keyList.add(itemsModelInstance);
A get()/contains() and so on may be somehow equal. I'd suggest you build your own Multimap<?,?> Implementation or just take an existing one, like the one from Guava (link above).
Kind regards,
avi