Android 在列表视图中从 Web 加载图像时出现 OutOfMemoryException

Android gives me `OutOfMemoryException` when loading images from web in a listview

我正在使用这个 ArrayAdapter 异步加载缩略图

public class CourierArrayAdapter extends ArrayAdapter<Courier> {
    private Map<String, Bitmap> bitmaps = new HashMap<>();

    private static class ViewHolder{
        TextView txtViewName;
        TextView txtViewNumber;
        CircleImageView imageViewPic;
        int position;
    }

    public CourierArrayAdapter(Context context, List<Courier> requests) {
        super(context, -1, requests);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        Courier request = getItem(position);

        if (convertView == null) {
            viewHolder = new ViewHolder();
            LayoutInflater layoutInflater = LayoutInflater.from(getContext());
            convertView = layoutInflater.inflate(R.layout.list_item_courier, parent, false);
            viewHolder.txtViewName = (TextView) convertView.findViewById(R.id.textView_list_item_courier_name);
            viewHolder.txtViewNumber = (TextView) convertView.findViewById(R.id.textView_list_item_courier_number);
            viewHolder.imageViewPic = (CircleImageView) convertView.findViewById(R.id.imageView_list_item_courier_pic);
            convertView.setTag(viewHolder);
        }
        else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.txtViewName.setText(request.name);
        viewHolder.txtViewNumber.setText(request.number);
        viewHolder.position = position;

        if (bitmaps.containsKey(request.picURL)) {
            viewHolder.imageViewPic.setImageBitmap(bitmaps.get(request.picURL));
        }
        else
            new LoadImageTask(position, viewHolder).execute(request.picURL);

        return convertView;
    }

    private class LoadImageTask extends AsyncTask<String, Void, Bitmap> {

        private int mPosition;
        private ViewHolder mViewHolder;


        public LoadImageTask(int position, ViewHolder holder) {
            this.mPosition = position;
            this.mViewHolder = holder;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            Bitmap bitmap = null;
            HttpURLConnection connection = null;

            try {
                URL url = new URL(params[0]); // create URL for image

                connection = (HttpURLConnection) url.openConnection();

                InputStream inputStream = connection.getInputStream();
                try {
                    bitmap = BitmapFactory.decodeStream(inputStream);
                    bitmaps.put(params[0], bitmap);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                finally {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                connection.disconnect(); // close the HttpURLConnection
            }

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (mViewHolder.position == mPosition)
                mViewHolder.imageViewPic.setImageBitmap(bitmap);
        }
    }
}

如您所见,我正在使用 picURL 属性 加载这些图像,并使用私有 AsyncTask class 加载它们。问题是我收到 OutOfMemoryException 一条错误消息:

FATAL EXCEPTION: AsyncTask #3
  java.lang.RuntimeException: An error occured while executing doInBackground()
      at android.os.AsyncTask.done(AsyncTask.java:304)
      at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
      at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
      at java.util.concurrent.FutureTask.run(FutureTask.java:242)
      at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:231)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
      at java.lang.Thread.run(Thread.java:818)
   Caused by: java.lang.OutOfMemoryError: Failed to allocate a 37602252 byte allocation with 16776720 free bytes and 29MB until OOM
      at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
      at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
      at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:863)
      at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:839)
      at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:877)
      at net.faraznet.provider.arrayadapters.CourierArrayAdapter$LoadImageTask.doInBackground(CourierArrayAdapter.java:99)
      at net.faraznet.provider.arrayadapters.CourierArrayAdapter$LoadImageTask.doInBackground(CourierArrayAdapter.java:76)
      at android.os.AsyncTask.call(AsyncTask.java:292)
      at java.util.concurrent.FutureTask.run(FutureTask.java:237)
      at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:231) 
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
      at java.lang.Thread.run(Thread.java:818)

图像有点大,如果可能的话,我在将它们加载到内存之前调整它们的大小不是问题,但问题是我不知道如何完成这个任务或任何其他任务我的问题的解决方案

您可以使用 Glide 推荐的第三方库 google 。要添加此库,请在 build.gradle 文件中的应用程序模块中添加以下依赖项。

dependencies {
    compile 'com.github.bumptech.glide:glide:3.5.2'
    compile 'com.android.support:support-v4:22.0.0'
}

然后您可以通过以下方式简单地加载图像

Glide.with(context)
    .load(request.picURL)
    .into(viewHolder.imageViewPic);

在依赖项中添加以下行。

compile 'com.squareup.picasso:picasso:2.5.2'

及以下用于在 ImageView 中获取图像的代码。

Picasso.with(context)
.load(url)
.into(imageView)

你可以开始了。有关详细信息,请查看此 link.

如果可以使用自定义库,则使用 Glide

Glide 在图像方面的性能和内存管理比 Picasso 更有效。检查这个 comparison

github中明确定义了在应用程序中使用Glide的所有步骤

或者,您可以将应用程序的堆大小增加 android:largeHeap="true"。但这不适用于任何 pre Honeycomb 设备

在 2.3 之前的设备上,您可以使用 VMRuntime class。但这不适用于姜饼及以上版本。

VMRuntime.getRuntime().setMinimumHeapSize(BIGGER_SIZE);

最近我实现了这个,所以实现这个的最好方法是使用 Picasso,在你的数组适配器中使用这个代码

Picasso.with(this)
            .load(url)
            .into(new Target() {
                @Override
                public void onBitmapLoaded (final Bitmap bitmap, Picasso.LoadedFrom from){
            /* Save the bitmap or do something with it here */

                    //Set it in the ImageView

                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {

                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {

                }
            });

在申请之前在清单中使用它。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

在依赖项中添加以下行或Gradle。

compile 'com.squareup.picasso:picasso:2.5.2'

通过以下链接获取信息

http://square.github.io/picasso/

https://github.com/square/picasso

希望对您有所帮助。