3

I'm building an app which downloads 2 images from a server, saves them to SD card, and displays them (cycling between, changing the image every 15 seconds).

I'm done, but after a couple minutes the program crashes: OutOfMemoryError: bitmap exceeds size of VM budget.

So, there must be a memory leak somewhere. I'm still fairly new to android programming: not too sure what cleanup I need to do that the VM won't.

Any help?

private ImageView mImageView;
private Bitmap mImageBitmap;
public static String rslt="";
/** Called when the activity is first created. */
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //Remove title bar
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);

    //Remove notification bar
    this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(R.layout.main);

    mImageView = (ImageView) findViewById(R.id.imageView1);

    mImageBitmap=null;

    final Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        int count = 1;
        public void run() {
            Bitmap bm = getNewImages();
            if (bm != null){
                mImageView.setImageBitmap(bm);
                bm=null;
                System.gc();
                handler.postDelayed(this, 15* 1000);
            }else if(count==1){
                File img1 = new File(Environment.getExternalStorageDirectory(), "1.jpg");
                if(img1.exists()){
                    bm = BitmapFactory.decodeFile(img1.getAbsolutePath());
                    if (bm != null){
                        mImageView.setImageBitmap(bm);
                        bm=null;
                    }
                }

                count = 2;
                System.gc();
                handler.postDelayed(this, 15* 1000);
            }else if (count==2){
                File img2 = new File(Environment.getExternalStorageDirectory(), "2.jpg");
                if(img2.exists()){
                    bm = BitmapFactory.decodeFile(img2.getAbsolutePath());
                    if (bm != null){
                        mImageView.setImageBitmap(bm);

                        bm=null;
                    }
                }

                count=1;
                System.gc();
                handler.postDelayed(this, 15* 1000);
            }

        }
        private Bitmap getNewImages() {
            ConnectivityManager connManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
            NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
            mImageBitmap=null;
            if (mWifi.isConnected()) {
                try{
                    //              EditText ed1=(EditText)findViewById(R.id.editText1);
                    //              String rego=ed1.getText().toString();
                    String rego="AGR905";
                    rslt="START";
                    Caller c=new Caller();
                    c.rego=rego;
                    c.join();
                    c.start();
                    while(rslt=="START")
                    {
                        try
                        {
                            Thread.sleep(10);
                        }
                        catch(Exception ex)
                        {
                        }
                    }
                }
                catch(Exception ex){
                }

                if (rslt.length()<=1600){
                }else{
                    byte[] decodedString = Base64.decode(rslt, Base64.DEFAULT);
                    mImageBitmap = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);

                    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                    mImageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);



                    File from = new File(Environment.getExternalStorageDirectory(), "1.jpg");
                    if(from.exists()){
                        File to = new File(Environment.getExternalStorageDirectory(), "2.jpg");
                        from.renameTo(to);
                        from.delete();

                    }
                    //you can create a new file name "test.jpg" in sdcard folder.
                    File f = new File(Environment.getExternalStorageDirectory()+ File.separator + "1.jpg");
                    try {
                        f.createNewFile();

                        //write the bytes in file
                        FileOutputStream fo = new FileOutputStream(f);
                        fo.write(bytes.toByteArray());

                        // remember close de FileOutput
                        fo.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return mImageBitmap;
        }
    };
    handler.postDelayed(runnable, 2000); //for initial delay..
kaderud
  • 5,457
  • 2
  • 36
  • 49
RHok
  • 152
  • 1
  • 2
  • 11

2 Answers2

3

There are a few things which should help to smooth the memory here.

  1. Run bitmap.recycle() after you are done with the bitmap.
  2. Afterwards, set them to null.
  3. Run the garbage collector each pass through. System.gc().
  4. Make sure you are requesting the large heap from the manifest. android:largeHeap="true" into the application.
  5. Work on memory profiling your application. See this answer for more details on how to accomplish this.
Community
  • 1
  • 1
PearsonArtPhoto
  • 38,970
  • 17
  • 111
  • 142
  • You may have forgotten to mention [Bitmap.recycle()](http://developer.android.com/reference/android/graphics/Bitmap.html#recycle()) – Andrii Chernenko Jan 31 '13 at 21:17
  • Sure, I'll ad that to the list... My first android program was a photo program, but that was a while ago... – PearsonArtPhoto Jan 31 '13 at 21:17
  • Adding the recycle is throwing errors or "trying to use recycled bitmap". Because there is a continuous loop, I don't want to be recycling them? or...? – RHok Jan 31 '13 at 21:31
  • The trick is where to recycle them. Make sure you recycle the image as late in the game as you can. – PearsonArtPhoto Jan 31 '13 at 21:34
  • Thanks for the help thus far by the way, I'm definitely getting somewhere. Without the recycles, it works decently, seemingly stable. It now only crashes when I turn the wifi either on or off, after it's been running for over 5min. Which confuses me a lot.. Any ideas on this one?>>I'll update my code now – RHok Jan 31 '13 at 21:42
  • Image viewing apps are a lot of work to get right... Take a look at this previous answer for some ideas. http://stackoverflow.com/a/9987237/544198 – PearsonArtPhoto Jan 31 '13 at 21:45
  • 1
    Seems to be working, thanks heaps! I still need to test it over a couple of days, but looking at the memory use in development modes, it seems to be fairly stable. – RHok Jan 31 '13 at 21:55
1

I don't think that the problem is memory leak. Probably you are exceeding your memory budget by holding both images in memory simultaneously. Remember, that each bitmap uses height_in_px * width_in_px * 4 bytes, while memory budget of your app is 24...48 MBytes (depends on the device).

There's a great tutorial on the efficient usage of bitmaps. Use it to find out what is the problem and to fix it.

Andrii Chernenko
  • 9,873
  • 7
  • 71
  • 89