【问题标题】:Android gives me `OutOfMemoryException` when loading images from web in a listview在列表视图中从 Web 加载图像时,Android 给了我“OutOfMemoryException”
【发布时间】:2016-09-08 07:06:33
【问题描述】:

我正在使用这个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 类加载它们。问题是我收到OutOfMemoryException 一条错误消息:

FATAL EXCEPTION: AsyncTask #3
  java.lang.RuntimeException: An error occured while executing doInBackground()
      at android.os.AsyncTask$3.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$1.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$2.call(AsyncTask.java:292)
      at java.util.concurrent.FutureTask.run(FutureTask.java:237)
      at android.os.AsyncTask$SerialExecutor$1.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)

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

【问题讨论】:

标签: android listview android-asynctask out-of-memory


【解决方案1】:

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

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

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

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

在 2.3 之前的设备上,您可以使用 VMRuntime 类。但这不适用于 Gingerbread 及更高版本。

VMRuntime.getRuntime().setMinimumHeapSize(BIGGER_SIZE);

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 如何将它们缓存到我的位图中HashMap?因为我只想加载一次图像?这个库是自己做的吗?
    • Glide 有缓存选项。只需添加 diskCacheStrategy(DiskCacheStrategy.ALL) 。所以它将是 Glide.with(context) .load(request.picURL) .diskCacheStrategy(DiskCacheStrategy.ALL) .into(viewHolder.imageViewPic);
    • 我不是指那种缓存。例如,我有图像 p1、p2 和 p3。在我的列表视图中,p3 图像重复了两次。我想要 Glide 加载的位图并重用它。我不想调用 Glide 方法两次
    • 对不起,我不确定。
    【解决方案3】:

    在依赖项中添加以下行。

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

    以及下面的代码来获取 ImageView 中的图像。

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

    你可以走了。欲了解更多信息,请查看此link

    【讨论】:

      【解决方案4】:

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

      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

      我希望它会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-25
        • 2012-07-19
        • 1970-01-01
        • 2023-03-28
        • 1970-01-01
        • 1970-01-01
        • 2016-10-05
        相关资源
        最近更新 更多