OutOfMemoryError thrown by multiple (> 120) calls to getView in GridView

I am using GridView to display images. Images are loaded from the feed and added to the BitmapCache. The GridView is inside a ViewFlipper (which has a ListView as its second view). I am using GridView for the first time, but I did a lot with adapters when using ListViews.

At the moment, the channel only feeds two images. But when I run my fragment containing the GridView, I get an OutOfMemoryError called by bei BitmapFactory.decodeStream (). As I delved deeper into the logarithm, I noticed that getView () inside my adapter for the GridView is being called many times. I know it's nothing special if getView () is called more than once, but the getView () method in my adapter gets called more than 120 times just for position 0. And I don't quite understand why it's called so often. But I'm pretty sure this caused my memory issue as this method tries to load the bitmap over 100 times in a few seconds.

Since I am already trying to refactor my view with the ViewHolder, I am completely helpless right now, and I hope someone can explain these bulk getView () calls to me and / or give me a hint to solve my problem.

GetView () is the mthod of my adapter:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;

    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    if (convertView == null) {
        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.pictures_grid_item, parent, false);
        holder.image = (ImageView) convertView.findViewById(R.id.picturesGridImage);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
        holder.image.setImageBitmap(null);
    }

    Picture picture = (Picture) pictureList.get(position);
    String imageUrl = picture.getUrl();

    if (!TextUtils.isEmpty(imageUrl)) {
        holder.image.setTag(imageUrl);
        ImageLoader.getInstance(context).loadImageWithTagCheck(holder.image);
    }

    return convertView;
}


private static class ViewHolder {
    ImageView image;
}

      

The loadImageWithTagCheck () method simply checks if the image has already been loaded (which ultimately should be)

Fragment that contains the view:

public class PicturesFragment extends BaseFragment {

private List<Parcelable> pictureList;
private PicturesGridAdapter adapter;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.pictures_fragment, container, false);
    // TODO: Remove final after development
    final MediaActivity activity = (MediaActivity) getActivity();

    pictureList = activity.getPictures();

    adapter = new PicturesGridAdapter(activity, pictureList);

    GridView gridview = (GridView) view.findViewById(R.id.picturesGrid);
    gridview.setAdapter(adapter);

    gridview.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            Toast.makeText(activity, "" + position, Toast.LENGTH_SHORT).show();
        }
    });
    return view;
}
}

      

By the way: I don't use * wrap_content * anywhere.

Edit: Here is the image uploader code. Of course ImageLoader is a problem causing outOfMemoryError. But I think the problem is more with the adapter, because 120 calls to getView () for position 0 immediately after the view is created cannot be right. The adapter is only created once, so there are> 120 calls in one instance of my adapter. (this is a rather large and complex project, so a "simple" image loader has a lot of code)

    public void loadImageWithTagCheck(final ImageView view) {
    final String url = (String) view.getTag();
    final Handler uiHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
        }
    };
    if (imageHandler != null) {
        imageHandler.post(new Runnable() {

            @Override
            public void run() {
                final Bitmap bmp = getImage(url, view);
                uiHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        String tagUrl = (String) view.getTag();
                        if (tagUrl.equals(url) && bmp != null
                                && !bmp.isRecycled()) {
                            scaleBitmapAndAdjustViewByHeight(view, bmp);
                        } else if (bmp != null) {
                            bmp.recycle();
                        }
                    }
                });
            }
        });
    }
}

     private Bitmap getImage(String url, View v) {
        Bitmap bmp = null;

        if (url != null && !TextUtils.isEmpty(url)) {
            String md5Url = Utility.md5(url);
            if (cache.containsKey(md5Url)) {
                bmp = cache.getBitmap(md5Url);
            } else {
                HttpGet httpGet = new HttpGet();
                HttpClient httpClient = new DefaultHttpClient();

                HttpResponse response = null;
                try {
                    URI uri = new URI(url);
                    httpGet.setURI(uri);
                    response = httpClient.execute(httpGet);

                    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                        HttpEntity entity = response.getEntity();
                        if (entity != null) {
                            final BufferedInputStream buffIn = new BufferedInputStream(
                                    entity.getContent(), Utils.IO_BUFFER_SIZE);
                            BitmapFactory.Options options = new BitmapFactory.Options();
                            options.inJustDecodeBounds = true;
                            options.outWidth = v.getWidth();
                            options.outHeight = v.getHeight();
                            options.inPurgeable = true;
                            options.inInputShareable = true;
                            options.inPreferredConfig = Bitmap.Config.RGB_565;

                            bmp = BitmapFactory.decodeStream(buffIn, null,
                                    options);
                        }
                    }
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                } catch (ClientProtocolException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (IllegalStateException e) {
                    e.printStackTrace();
                }

                if (bmp != null) {
                    cache.put(md5Url, bmp);
                }
            }
        }

        return bmp;
    }

private void scaleBitmapAndAdjustViewByHeight(final ImageView view,
        final Bitmap bmp) {
    ViewTreeObserver vto = view.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        @SuppressLint("NewApi")
        @SuppressWarnings("deprecation")
        @Override
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(
                        this);
            } else {
                view.getViewTreeObserver().removeGlobalOnLayoutListener(
                        this);
            }

            // Get current dimensions
            int width = bmp.getWidth();
            int height = bmp.getHeight();

            // Determine how much to scale: the dimension requiring less
            // scaling is closer to the its side. This way the image always
            // stays inside your bounding box AND either x/y axis touches
            // it.
            int imageViewHeightFromXMLinPixels = view.getHeight();
            float xScale = (float) ((imageViewHeightFromXMLinPixels * 2.75) / width);
            float yScale = ((float) imageViewHeightFromXMLinPixels)
                    / height;
            float scale = (xScale <= yScale) ? xScale : yScale;

            // Create a matrix for the scaling and add the scaling data
            Matrix matrix = new Matrix();
            matrix.postScale(scale, scale);

            // Create a new bitmap
            Bitmap scaledBitmap = Bitmap.createBitmap(bmp, 0, 0, width,
                    height, matrix, true);
            width = scaledBitmap.getWidth(); // re-use

            view.setImageBitmap(scaledBitmap);
            view.getLayoutParams().width = width;
        }
    });
    view.requestLayout();
}

      

+3


source to share


1 answer


Get rid of the method scaleBitmapAndAdjustViewByHeight(...)

. Do a simple one instead view.setImageBitmap(bmp)

.



Why? scaleBitmapAndAdjustViewByHeight(...)

calls view.requestLayout()

what probably calls your adapters getView(...)

and ends up deadlocked and finally OutOfMemoryError.

+2


source







All Articles