Mir
Mir

Reputation: 55

Out of Memory Error - Optimization?

My application crashes when I try to navigate to other activities. I believe it's due to the number of pictures I have in my drawables and setting on my application. I have a ListView and an ArrayAdapter set up so I'm not sure why it's using so much memory. Any ideas?

10-29 20:56:52.287 18391-18391/? E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.android.tourguide, PID: 18391 java.lang.OutOfMemoryError: Failed to allocate a 366378124 byte allocation with 16777216 free bytes and 24MB until OOM

Here's my code:

Main Activity

package com.example.android.tourguide;

import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set the content of the activity to use the activity_main.xml layout file
    setContentView(R.layout.activity_main);

    ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
    CategoryAdapter adapter = new CategoryAdapter(this, getSupportFragmentManager());
    viewPager.setAdapter(adapter);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(viewPager);
}

}

Fragment

package com.example.android.tourguide;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.RatingBar;

import java.util.ArrayList;


/**
 * A simple {@link Fragment} subclass.
 */
public class RestaurantsFragment extends Fragment {


    public RestaurantsFragment() {
        // Required empty public constructor
    }


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment

    View rootView = inflater.inflate(R.layout.word_list, container, false);

    final ArrayList<Information> words = new ArrayList<Information>();

    words.add(new Information(R.drawable.restaurant_1, "Rashid's House",     "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_2, "Moo Moo Cow", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_3, "Burger 7", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_4, "Mini Horse", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_5, "Prime Prime!", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_6, "Halal Cart 96", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_7, "Half Moon", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_8, "Mourice's Steakhouse", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_9, "The Rudeboy", "Description", R.drawable.five_star_rating));
    words.add(new Information(R.drawable.restaurant_10, "Meowth's Lair", "Description", R.drawable.five_star_rating));

    InformationAdapter itemsAdapter = new InformationAdapter(getActivity(), words);
    ListView listView = (ListView) rootView.findViewById(R.id.list);
    listView.setAdapter(itemsAdapter);

    return rootView;
}

}

InformationActivity

package com.example.android.tourguide;

import static android.R.attr.description;
import static com.example.android.tourguide.R.id.place;

/**
 * Created by msanli on 10/27/2016.
 */

public class Information {

private int mImageResourceId;
private String mPlace;
private String mDescription;
private int mRatingBarResourceId;

public Information(int imageResourceId, String place, String description, int ratingBarResourceId ){
    mImageResourceId = imageResourceId;
    mPlace = place;
    mDescription = description;
    mRatingBarResourceId = ratingBarResourceId;

}

public int getImageResourceId() {
    return mImageResourceId;
}

public String getPlace(){
    return mPlace;
}

public String getDescription(){
    return mDescription;
}

public int getRatingBarResourceId(){
    return mRatingBarResourceId;
}

}

InformationAdapter

package com.example.android.tourguide;

import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.RatingBar;
import android.widget.TextView;

import java.util.ArrayList;

/**
 * Created by msanli on 10/27/2016.
 */

public class InformationAdapter extends ArrayAdapter<Information> {



public InformationAdapter(Activity context, ArrayList<Information> words){
    super(context, 0, words);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View listItemView = convertView;
    if(listItemView == null) {
        listItemView = LayoutInflater.from(getContext()).inflate(
                R.layout.list_item, parent, false);
    }

    // Get the {@link Word} object located at this position in the list
    Information currentInformation = getItem(position);

    ImageView imageImageView = (ImageView) listItemView.findViewById(R.id.image_place);
    imageImageView.setImageResource(currentInformation.getImageResourceId());
    // Find the TextView in the list_item.xml layout with the ID version_name
    TextView nameTextView = (TextView) listItemView.findViewById(R.id.place);
    // Get the version name from the current Word object and
    // set this text on the name TextView
    nameTextView.setText(currentInformation.getPlace());

    // Find the TextView in the list_item.xml layout with the ID version_number
    TextView descriptionTextView = (TextView) listItemView.findViewById(R.id.description);
    // Get the version number from the current Word object and
    // set this text on the name TextView
    descriptionTextView.setText(currentInformation.getDescription());

    ImageView ratingBarImageView = (ImageView) listItemView.findViewById(R.id.ratings_bar);
    ratingBarImageView.setImageResource(currentInformation.getRatingBarResourceId());

    View textContainer = listItemView.findViewById(R.id.text_container);

    return listItemView;

}
}

CategoryAdapter

package com.example.android.tourguide;

import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

/**
 * Created by msanli on 10/29/2016.
 */

public class CategoryAdapter extends FragmentPagerAdapter {
private Context mContext;

public CategoryAdapter(Context context, FragmentManager fm){
    super(fm);
    mContext = context;
}

@Override
public Fragment getItem(int position) {
    if (position == 0) {
        return new RestaurantsFragment();
    } else if (position == 1) {
        return new ParksFragment();
    } else if (position == 2) {
        return new EntertainmentFragment();
    } else {
        return new AttractionsFragment();
    }
}

@Override
public int getCount() {
    return 4;
}

@Override
public CharSequence getPageTitle(int position) {
    if (position == 0) {
        return mContext.getString(R.string.category_restaurants);
    }else if (position == 1){
        return mContext.getString(R.string.category_parks);
    }else if (position == 2){
        return mContext.getString(R.string.category_entertainment);
    }else{
        return mContext.getString(R.string.category_attractions);
    }

}
}

Upvotes: 0

Views: 327

Answers (2)

Ali
Ali

Reputation: 3241

The problem you face is the result of two things:

  1. Android works with images in uncompressed form. An image allocates byte-array that is basically proportional to the number of pixels in it. For example if you have 800x640 image you get 0.5 MB byte-array in memory.

  2. Your drawable-dpi folder does not match the screen dpi, thus the image is scaled to match the screen dpi, increasing the size of the allocated byte-array by multiple times.

Assume you store your 800x640 image in drawable (or drawable-mdpi) which corresponds to 160dpi (i.e. physical image dimensions are 5 x 4 inches):

  • Scaling up will happen on the device with 320dpi screen resolution: since the screen has twice more pixels per inch than the image, you will have 1600x1280 pixels allocated for the image in memory (keeping physical image dimensions 5 x 4 inches).
  • Scaling down will happen on the device with 120dpi screen resolution: since the screen has 1/4 less pixels per inch than the image, you will have 600x480 pixels allocated for the image in the memory (keeping physical image dimensions 5 x 4 inches).

As Shuvro wrote you can avoid scaling by putting your images to drawable-nodpi folder. The downside is that you will waste memory for the devices with lower resolution, since the corresponding physical dimensions will be bigger than needed.

IIUC you have a list of thumbnails and you use full images from drawable to populate it. The proper solution would be to use thumbnail versions of your images in corresponding dpi folders. That will minimize amount of allocated memory and increase the performance by avoiding scaling.

See more: https://developer.android.com/guide/practices/screens_support.html

Upvotes: 0

Mithun Sarker
Mithun Sarker

Reputation: 4023

Try this, where you drawable folder is located (app->src->main->main->res) , create a folder named it drawable-nodpi , put your all the images there . Let me know if it works for you

Upvotes: 1

Related Questions