ClassCastException when unmarshelling parcelable array - android

I'm basically trying to save an array of Bitmaps between states.
My fragment's onSaveInstanceState:
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArray(SELECTED_IMAGES_ARRAY_BUNDLE, galleryAdapter.getImageBitmaps());
}
And in it's onCreateView I retrieve the array as so:
savedInstanceState.setClassLoader(Bitmap.class.getClassLoader());
Bitmap[] savedSelectedImages = (Bitmap[]) savedInstanceState.getParcelableArray(SELECTED_IMAGES_ARRAY_BUNDLE);
This WORKS regularly, EXCEPT when the Android OS does some memory management, kills off my process if it's running in the background, and later tries to restore it when I come back to it.
This is the error I get:
AndroidRuntime(4985): Caused by: java.lang.ClassCastException: android.os.Parcelable[] cannot be cast to android.graphics.Bitmap[]
I thought it was something to do with not properly setting the classLoader, but I tried everything and can't seem to get it to work

Hi It is not recommended to put the Bitmap in a bundle that cost extra processing time. And consumes lot of memory.. Highly recommended to pass the path or save the image in the External storage and and bundle the absolute path to the image file....check this question in SO Bundle Bitmap .

You can do something like this.Where p is your parcelable list. And finally convert list of bitmaps back to an array.
List<Parcelable> pList = Arrays.asList(p);
ArrayList<Bitmap> bList = new ArrayList<Bitmap>();
for(Parcelable px : pList){
bList.add((Bitmap)px);
}

Related

Restoring an ArrayList of custom objects

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

Safely deserializing LinkedHashMap in android

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.

ViewFlipper not loading bitmap images

Hello all having some trouble when I attempt to add an image to a viewflipper page, I am pulling the bitmaps from the db4o database (not sure if it is the encoding or something it uses that is messing me up).
private void setImageView() {
page = (ViewFlipper) findViewById(R.id.viewFlipper1);
int temp = DigPlayDB.getInstance(getBaseContext()).getPlaysDBSize();
for(int j = 0; j < temp; ++j){
test.add(DigPlayDB.getInstance(getBaseContext()).getPlayByInt(j).getImage());
test1.add(DigPlayDB.getInstance(getBaseContext()).getPlayByInt(j).getPlayName());
}
for(int i=0;i<temp; i++)
{
// This will create dynamic image view and add them to ViewFlipper
setFlipperImage(test.get(i));
}
And then for the setting of the image and adding the view to the page
private void setFlipperImage(Bitmap image){
ImageView _image = new ImageView(getApplicationContext());
//_image.setBackgroundDrawable(new BitmapDrawable(getResources(), image));
_image.setImageBitmap(image);
page.addView(_image);
Log.d("db", "" + image);
}
It works right after I add an image to the database but just that image, older images as well as when I restart the application do not load up even though it says they do from a debugging log I set. I am thinking that the last one shows up since it could still be in a cache somewhere, but the older ones that are stored in the database and not in a cache are not encoded correctly or something. Any help would be awesome. Thanks!
Edit: I should mention that "test" is an arraylist of Bitmaps.
Ok, you said in the comment that you store the object as a Bitmap instance. I guess thats a Android or library class.
Don't do that. Only store instances of your own classes. Storing instances of your classes, java.util.collections, arrays and primitives are okay. Everything else is bound to issues: db4o will eagerly try to store any object. This is a issue for library instances. You don't have control of what they do, how they work internally and if they still work after loading.
I think that's whats happening here. As long as the application is running, db4o returns the cached instance of the object, which is fine. After restarting the application, db4o loads the Bitmap object. However the bitmap object isn't intended to be stored with db4o, so it stumbles over wrongly stored internal state.
So, store your picture in a byte-array. Or just as plain file on the SD-card.

Android memory usage

i am building an android application but i have some questions about
the memory usage.
Most of the data i need and use are string arrays stored in the xml strings file. I used arrays because firstly the biggest array will have up to 30 items and secondly there won't be any updating or deleting or inserting items through the app.
All the custom adapters i created are following googles' guidlines (the fast way - using the holder class)
As the user switches between the activities, depending on the selections he makes different arrays are loaded in the listviews.
Does android clears the memory each array allocates if its not in use? should i do that?
I ve used MAT also to check how the app uses memory and to check for leaks etc..and i thing that everything is fine. I also use a few png icons/images.
The app gets 5MB when it starts, goes up and down up to 8.5-9MB as the user plays around.
Thanks in advance for any help!
It's possible the Android OS will kill your Activities (without focus) on the stack if memory is needed. When your Activity is killed in this way, onSaveInstanceState(Bundle outState) is called. You should save your string array here.
When onCreate(Bundle savedInstanceState) is called in your Activity, if savedInstanceState is not NULL, then it means your Activity was previously killed by the OS and you need to repopulate your string array from that bundle.
ex:
String [] stringArray;
...
protected void onCreate(Bundle savedInstanceState)
{
if (savedInstanceState != null)
{
stringArray = savedInstanceState.getStringArray("some_key");
}
}
protected void onSaveInstanceState (Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putStringArray("some_key", stringArray);
}
This is described in more detail here: http://developer.android.com/reference/android/app/Activity.html

android: cleaning up memory on app destroy

I am developing an app which instantiates a bunch of bitmap objects (e.g. buttons, which have cache bitmaps, so they don't have to get rendered again and again)
Now, I realised that when I run and start the app repeatedly on my huawei mobile device, I get an OutOfMemoryException at a point where the app tries to allocate some memory for the bitmaps.
So I guess it's the bitmaps which make trouble. I do know that there is a bitmap.recycle() method though.
Now my question: what is best practice to clean up memory?
Why isn't there some View method like View::onDestroy() which can be implemented for cleanup purpose?
EDIT: example
my "CirclyButton" (extends Button) class always draws a cached bitmap onDraw:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(this.getDefaultBitmap(), 0, 0, paint);
}
private Bitmap getDefaultBitmap(){
if(mBitmapDefault == null){
mBitmapDefault = Bitmap.createBitmap(8*radius, 8*radius, Config.ARGB_8888);
Canvas canvas = new Canvas(mBitmapDefault);
this.drawDefault(canvas);
return mBitmapDefault;
}
return mBitmapDefault;
}
So I guess this allocated data should be recycled somewhere...?
Views don't have an onDestroy method because views usually don't get destroyed, activities do. A view won't just be destroyed if nothing happens to its activity (Unless you inflate a different layout... That's not the case, right?), and if something happens to its activity, you do have a callback getting called.
If there is a recycle() method, make sure you call it. And remove all reference to memory taking objects in the onDestroy, i.e:
#Override
public void onDestroy() {
object1 = null;
object2 = null;
//...
}
So the GC can do its job. I had the same problem with the AdView of AdMob, although they did have a destroy method it didn't really help. But deleting my references of the view fixed the problem.
Provide more information about where are you using your bitmaps, i have some serious experience of working with images and saving memory.
For example in my app i have a list of some data, which display some bitmap in each row. I store my list in a fragment(for fragment support i use compatibility library), and i recycled my bitmaps on this fragment onDestroy method.
Later i decided to optimize my list, so i added scroll listener to my list and started recycling bitmaps, when they are scrolled off the screen.

Categories

Resources