views:

18995

answers:

13

I am not sure where to start to explain this one.

I have a list view with a couple image buttons on each row. When you click the list row, it launches a new activity. If you review some of my other posts, I have had to build my own tabs because of an issue w/ the camera layout. The activity that gets launched for result is a map. If I click on my button to launch the image preview (load an image off the sd card) the application returns from the activity back to the listview activity to the result handler to relaunch my new activity which is nothing more than an image widget.

So here is the issue, the image preview on the list view is being done w/ the cursor & listadapter. This makes it pretty simple, but I am not sure how I can put a resized (i.e. smaller bit size not pixel) image as the src for the imgbutton on the fly. So I just resized the image that came off the phone camera.

The issue is that I get an out of memory error when it tries to go back and re-launch the 2nd activity.

** My question : is there a way I can build the list adapter easily row by row, where I can resize on the fly (bit wise)? - this would be preferable as I also need to make some changes to the properties of the widgets/elements in each row as I am unable to select a row w/ touch screen b/c of focus issue. (I can use roller ball).

** I know I can do an out of band resize and save of my image, but that is not really what I want to do, but some sample code for that would be nice if that is your suggestion.

As soon as I disabled the image on the listview it worked fine again.

FYI : This is how I was doing it :

            String[] from = new String[] { DBHelper.KEY_BUSINESSNAME, DBHelper.KEY_ADDRESS, DBHelper.KEY_CITY, DBHelper.KEY_GPSLONG, DBHelper.KEY_GPSLAT,  DBHelper.KEY_IMAGEFILENAME  + ""};
        to = new int[] { R.id.businessname, R.id.address, R.id.city, R.id.gpslong, R.id.gpslat, R.id.imagefilename };
         notes =
            new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
        setListAdapter(notes);

Where R.id.imagefilename is a ButtonImage

Here is my LogCat


01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM won't let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed


I also have a new error when displaying an image :

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d

01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process. 01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes 01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

A: 

I did the following to take the image and resize it on the fly. Hope this helps

 Bitmap bm;
 bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath),100, 100, true);
 mPicture = new ImageView(context);
 mPicture.setImageBitmap(bm);
Chrispix
This approach scales the bitmap. But it doesn't solve the OutOfMemory issue because the full bitmap is being decoded anyway.
Fedor
I will see if I can look at my old code, but I think it did solve my out of memory issues. Will double check my old code.
Chrispix
+36  A: 

To fix OutOfMemory you should do something like that:

BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap=BitmapFactory.decodeStream(is,null,options);

This inSampleSize option reduces memory consumption.

Here's a complete method. First it reads image size without decoding the content itself. Then it finds the best inSampleSize value, it should be a power of 2. And finally the image is decoded.

//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
    try {
        //Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f),null,o);

        //The new size we want to scale to
        final int REQUIRED_SIZE=70;

        //Find the correct scale value. It should be the power of 2.
        int width_tmp=o.outWidth, height_tmp=o.outHeight;
        int scale=1;
        while(true){
            if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                break;
            width_tmp/=2;
            height_tmp/=2;
            scale++;
        }

        //Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize=scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}
Fedor
Note that 10 may not be the best value for inSampleSize though, the documentation suggests using powers of 2.
Mirko Nasato
Right. I've edited the answer.
Fedor
I love you, Fedor!!!
Cristian
I'm facing the same problem as Chrispix, but I don't think the solution here really solves the problem, but rather sidesteps it. Changing the sample size reduces the amount of memory used (at the cost of image quality, which is probably okay for an image preview), but it will not prevent the exception if a large enough image stream is decoded, of if multiple image streams are decoded.If I find a better solution (and there may not be one) I'll post an answer here.
Flynn81
You only need an appropriate size to match the screen in pixel density, for zooming in and such you can take a sample of the image at a higher density.
stealthcopter
A: 

I had the same problem and Fedor's solution is effective.

Jay Pogi
A: 

u should first clear all the view... than u can try... means bcz of mobile memory is limited... bitmap object consumes more memory compare to other. i had getting the same problem but after removing all previously created view it worked correctly ... Enjoy....

Ashish Mishra
A: 

hello, as I'm beginner in Android I have problem with memory - after only 2-3 minutes I get Force Close and outOfMemory Error.

I have only onCreate (I know, stupid, but I didn't knew for anything else as I started only few weeks ago) and inside I have...

@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  many lines of code...manipulating with SQLite databases...

}

inside main XML layout I have many images, small, big (background) and so on...How can I on every onCreate "delete" all content from memory that was before in it - so when I open activity again, it deletes all images and everything out and insert the new (old) one inside. "Little" awkward but that's only thing I have on mind.

Also, inside "many lines of code" I don't declare any images!

Or simply, how can I "bypass" outOfMemoryError? Do I have to do something like this?

 @Override
     public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

        BitmapFactory.Options options=new BitmapFactory.Options();
        options.inSampleSize = 8;
        Bitmap buildingBitmap = BitmapFactory.decodeResource(null,R.drawable.background,options);

if (buildingBitmap != null)
        {
        buildingBitmap.recycle();
        System.gc();
        buildingBitmap = null;
        }

      setContentView(R.layout.main);

      many lines of code...manipulating with SQLite databases...

    }

or...? Also on other activity is everything "the same" except I have multiple overlays, how can I "erase" (recycle) them also? Thank you for any help.

*currently, I'm not searching for fastest, most "correct" or better solution, I just want something that it's not crashing all the time

svebee
this should be a separate question... not an answer to a related question
gary comtois
A: 

Hello Fedor, I was having the same issue and your proposed solution worked for me but now image's quality is very poor. How to deal with it?

Sam
+2  A: 

May be the answer mentioned on this site might solve the issue...

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];

bitmapImage = BitmapFactory.decodeFile(path,opt);
Sash
Tried this solution. Unfortunately no effect. Still OutOfMemory for large photos.
Fedor
I also tried this and still get an OutOfMemory exception too.
stealthcopter
I don't think this helps, and the reason I don't is because I read the source code for decodeFile and the source code does this itself if you don't.
Max Howell
A: 

Fedor.... Thnaks a lot.... Excellent solution.

Bilal
+1  A: 

You are most likely suffering from this Android bug:

http://code.google.com/p/android/issues/detail?id=8488

The bug report contains a testcase.

Andreas Schildbach
+4  A: 

I've made a small improvement to Fedor's code. It basically does the same, but without the (in my opinion) ugly while loop and it always results in a power of two. Kudos to Fedor for making the original solution, I was stuck until I found his, and then I was able to make this one :)

private Bitmap decodeFile(File f){
    Bitmap b = null;
    try {
        //Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);
        int scale = 1;
        if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
            scale = Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
        }

        //Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {
    }
    return b;
}
Thomas Vervest
Yes you're right while is not so beautiful. I just tried to make it clear to everyone. Thanks for your code.
Fedor
This (and Fedor's code) helped point me in the right direction. Thanks!
Jake Basile
@Thomas Vervest - There's a big problem with that code. ^ doesn't raise 2 to a power, it xors 2 with the result. You want Math.pow(2.0, ...). Otherwise, this looks good.
DougW
Ooh, that's a very good one! My bad, I'll correct it immediately, thanks for the reply!
Thomas Vervest
A: 

Hey Sam, Your problem is probably because of the scale value. It should always be a power of two, if you use my code it should solve your problem :)

Thomas Vervest
A: 

Can anybody help with another problem - it seemes that some memory(especially using list adapters with bitmaps) doesn't release even when you "null'ing" adapters and recycling bitmaps. Is there any strategy for an absolute memory cleaning? Resizing is not an option because bitmaps already have small size... anyway thnx for the help.

Alexander