DaveTheMinion
DaveTheMinion

Reputation: 664

Android App OutOfMemory Error

I am writing an Android app that requires large amounts of memory, and is experiencing issues with running out of memory. The app has two activities. The main activity uses a series of buttons that use event handlers to load different images in to ImageViews in the second activity. When the user presses the Android back button, the app returns to the main activity.

The problem that I am having is that eventually, the app crashes with an OutOfMemory error.

The following graph shows my app's memory usage as I go back and forth between the two activities. After taking this screenshot, I attempted one more time to open the second activity through one of the buttons, and the error occurred again. You'll notice that the last hump is further away from the others. This is because I stopped for a second to take a screenshot with the expectation that the next attempt would crash it. When it didn't I screenshoted again.

Android App Memory Usage Graph

I would assume that the problem could be resolved by explicitly forcing the activities to free the memory when they are not being used. The problem with this is that I do not how to do this, and cannot find any clear explanation on line that details how this could be done.

Here is a sample of the code from the Image View activity:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
import android.widget.TableRow;

import java.io.File;

public class ViewPost extends AppCompatActivity {
    Context appContext = this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_image);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    @Override
    protected void onResume() {
        super.onResume();

        new Thread(new Runnable() {
            @Override
            public void run() {
                final ImageView imgPhoto = (ImageView) findViewById(R.id.imgPhoto);
                final TableRow photoLayout = (TableRow) findViewById(R.id.photoLayout);

                // Get the width of the device screen.
                Point screenDimensionFinder = new Point();
                getWindowManager().getDefaultDisplay().getSize(screenDimensionFinder);
                final int screenWidth = screenDimensionFinder.x;

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        File imageFolder = new File(String.valueOf(appContext.getExternalFilesDir(
                                Environment.DIRECTORY_PICTURES)));
                        File photoFile = new File(imageFolder, "P" + Integer.toString(postId) +
                                ".jpg");
                        if(photoFile.exists()) {
                            Bitmap image = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
                            imgPhoto.setImageBitmap(resizeBitmap(image, screenWidth));
                        }
                        else {
                            photoLayout.setVisibility(View.GONE);
                        }
                    }
                });
            }
        }).start();
    }

    private Bitmap resizeBitmap(Bitmap image, int screenWidth)
    {
        // Resize the bitmap so that it fits on the screen, while preserving its aspect ratio.
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();
        int imageNewHeight = (imageHeight * screenWidth) / imageWidth;
        image = Bitmap.createScaledBitmap(image, screenWidth, imageNewHeight, false);
        return image;
    }
}

Upvotes: 0

Views: 83

Answers (2)

Jim Bockerstette
Jim Bockerstette

Reputation: 67

I had the exact same problem. I solved it by using Picasso by square. Picasso.with(getContext()).load(imageResId).fit().centerInside().into(imageView);

You're going to have to change how you load it but it supports load(File f).

Upvotes: 0

varunkr
varunkr

Reputation: 5542

You should try using this function for decoding the file. This method loads a scaled down version of the bitmap using the inSampleSize variable which reduces the memory consumption.

private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(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;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

Upvotes: 1

Related Questions