0

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;
    }
}
7
  • can you post the codes of the two activities ! Commented May 7, 2016 at 23:56
  • @varunkr What part would you like to see? I do not want to post all of it. (ie. event handlers for buttons, image handling) Commented May 8, 2016 at 0:02
  • Since you are posting images, it is but obvious that it is the source of error, however to identify the exact problem I will need to look at the code.. Commented May 8, 2016 at 0:05
  • 1
    AT least code of second activity would be needed Commented May 8, 2016 at 0:06
  • Have you read: developer.android.com/training/displaying-bitmaps/… Commented May 8, 2016 at 0:13

2 Answers 2

1

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;
}
Sign up to request clarification or add additional context in comments.

15 Comments

Thank you! Could you please explain to me what IMAGE_MAX_SIZE is? Is this the same function that I see on this page under the "Use an existing bitmap" header?
@DavidB No, the decodeFile there and one in your code are just using the default implementation in the BitmapFactory class which does not take care o this. In this method the image is being scaled appropriately so as to prevent memory errors. IMAGE_MAX_SIZE is the new size you want to scale it to.aka your imageView dimensions.
@DavidB Also I must say this to clarify your doubt in the question. SImply killing the activity doesn't guarantee that the memory will be cleared. To do that you will need to clear thr process which you should never do, instead leave it to the OS.
Very well. The thing is, the ImageView matches whatever the width of the device screen is, and I want to maintain the image's aspect ratio. Will that be a problem when using this?
You should try it once to see how the image looks. Aspect ratio shouldn't be a problem IMO. Quality might be an issue though you should try it to be sure.
|
0

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).

1 Comment

Thank you, but unfortunately, I am not allowed to use any third party libraries for this project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.