OCN Yang
OCN Yang

Reputation: 31

OutOfMemoryError When I use Recyclerview to load a lot of pictures

Logcat show:报错日志如下

04-18 10:04:04.987 3345-3345/? E/dalvikvm-heap: Out of memory on a 36480012-byte allocation.
04-18 10:04:05.019 3345-3345/? E/InputEventReceiver: Exception dispatching input event.
04-18 10:04:05.023 3345-3345/? E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback
04-18 10:04:05.031 3345-3345/? E/MessageQueue-JNI: java.lang.OutOfMemoryError
                       at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
                       at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:503)
                       at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:356)
                       at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:800)
                       at android.content.res.Resources.loadDrawable(Resources.java:2105)
                       at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
                       at android.widget.ImageView.<init>(ImageView.java:127)
                       at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:60)
                       at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:56)
                       at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:106)
                       at android.support.v7.app.AppCompatDelegateImplV9.createView(AppCompatDelegateImplV9.java:1021)
                       at android.support.v7.app.AppCompatDelegateImplV9.onCreateView(AppCompatDelegateImplV9.java:1080)
                       at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:47)
                       at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:684)
                       at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
                       at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
                       at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
                       at com.chad.library.adapter.base.BaseQuickAdapter.getItemView(BaseQuickAdapter.java:1042)
                       at com.chad.library.adapter.base.BaseQuickAdapter.createBaseViewHolder(BaseQuickAdapter.java:674)
                       at com.chad.library.adapter.base.BaseMultiItemQuickAdapter.onCreateDefViewHolder(BaseMultiItemQuickAdapter.java:48)
                       at com.chad.library.adapter.base.BaseQuickAdapter.onCreateViewHolder(BaseQuickAdapter.java:551)
                       at com.chad.library.adapter.base.BaseQuickAdapter.onCreateViewHolder(BaseQuickAdapter.java:57)
                       at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6319)
                       at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5507)
                       at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5392)
                       at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5388)
                       at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2149)
                       at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:556)
                       at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1496)
                       at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1313)
                       at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1058)
                       at android.support.v7.widget.GridLayoutManager.scrollVerticallyBy(GridLayoutManager.java:381)
                       at android.support.v7.widget.RecyclerView.scrollByInternal(RecyclerView.java:1680)
                       at android.support.v7.widget.RecyclerView.onGenericMotionEvent(RecyclerView.java:2932)
                       at android.view.View.dispatchGenericMotionEventInternal(View.java:7479)
                       at android.view.View.dispatchGenericMotionEvent(View.java:7460)
                       at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1819)
                       at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1772)
                       at android.view.View.dispatchGenericMotionEvent(View.java:7453)
                       at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1819)
                       at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1772)
                       at android.view.View.dispatchGenericMotionEvent(View.java:7453)
                       at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1819)
                       at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1772)
                       at android.view.View.dispatchGenericMotionEvent(View.java:7453)
                       at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1819)
                       at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1772)
                       at android.view.View.dispatchGenericMotionEvent(View.java:7453)
                    at android.view.Vie

The layout is:A RecyclerView in a Fragment,and the RecyclerView's item is ImageView

The Recyclerview's Adapter Load the content (I am using BaseRecyclerViewAdapterHelper this framework):

@Override
protected void convert(BaseViewHolder helper, WechatItem.ResultBean.ListBean item) {
    switch (helper.getItemViewType()) {
        case WechatItem.ResultBean.ListBean.STYLE_BIG:
            helper.setText(R.id.title_wechat_style1, TextUtils.isEmpty(item.getTitle()) ? mContext.getString(R.string.wechat_select) : item.getTitle());
            if (!isNotLoad) {           //Controls whether the image is loaded
                Glide.with(mContext.getApplicationContext())
                        .load(item.getFirstImg())
                        .override(mImgWidth, mImgHeight)
                        .placeholder(R.drawable.lodingview)
                        .error(R.drawable.errorview)
                        .crossFade(1000)
                        .into((ImageView) helper.getView(R.id.img_wechat_style));
            }
            break;
        case WechatItem.ResultBean.ListBean.STYLE_SMALL:
            helper.setText(R.id.title_wechat_style2, TextUtils.isEmpty(item.getTitle()) ? mContext.getString(R.string.wechat_select) : item.getTitle());
            if (!isNotLoad) {
                Glide.with(mContext.getApplicationContext())
                        .load(item.getFirstImg())
                        .placeholder(R.drawable.lodingview)
                        .error(R.drawable.errorview)
                        .override(mImgWidth / 2, mImgWidth / 2)
                        .crossFade(1000)
                        .into((ImageView) helper.getView(R.id.img_wechat_style));
            }
            break;
        default:
            break;
    }
}

When the phone memory is large, and will not appear OOM, when the phone memory is small, it is prone to OOM. 当手机内存较大时,并不会出现OOM,当手机内存较小时,很容易出现OOM。

Upvotes: 1

Views: 2154

Answers (2)

Ian Manyama
Ian Manyama

Reputation: 7

Try allocate large memory for your application, you can add android:largeHeap="true" on application tag inside your manifest.xml file

Upvotes: -1

Waqas Ahmed Ansari
Waqas Ahmed Ansari

Reputation: 1699

It is very clear from the Log, you are trying to allocate more bytes than a phone can bear. Try showing only (low quality) thumbnail in RecyclerView. You can show high-resolution image afterwards when user tap on RecyclerView's item.

You can use the following code to scale down the image to your desired width and height.

public static Bitmap decodeSampledBitmap(String filePath, int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    Bitmap scaled = BitmapFactory.decodeFile(filePath, options);
    return scaled;
}

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

Use function decodeSampledBitmap to scale down the image.

Upvotes: 1

Related Questions