35

我是 Android 编程新手,我收到一个错误,提示我的应用程序内存不足,这个示例是我从一本书中复制的,它使用的是小图片分辨率,但是当我添加了几张分辨率更高的图片时出现内存错误,可能是我做错了什么,或者只是不知道我应该使用图像的所有内容,如果有人知道我应该更改什么以便不会再次出现此错误,请帮助。谢谢期待!

源代码:

public class ImageViewsActivity extends Activity {
//the images to display
Integer[] imageIDs={
        R.drawable.pic1,
        R.drawable.pic2,
        R.drawable.pic3,
        R.drawable.pic4,
        R.drawable.pic5
};  
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final ImageView iv=(ImageView) findViewById(R.id.image1);

    Gallery gallery=(Gallery) findViewById(R.id.gallery);
    gallery.setAdapter(new ImageAdapter(this));
  gallery.setOnItemClickListener(new OnItemClickListener(){
        public void onItemClick(AdapterView<?> parent, View v, int position, long id){
            Toast.makeText(getBaseContext(), "pic"+(position+1)+" selected", Toast.LENGTH_SHORT).show();

            //display the image selected
            try{iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
              iv.setImageResource(imageIDs[position]);}catch(OutOfMemoryError e){
                     iv.setImageBitmap(null);
                }
        }
    });


}

public class ImageAdapter extends BaseAdapter{
    private Context context;
    private int itemBackground;

    public ImageAdapter(Context c){
        context=c;
        //setting the style
        TypedArray a = obtainStyledAttributes(R.styleable.Gallery1);
        itemBackground = a.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 0);
        a.recycle();
    }

    //returns the number of images
    public int getCount() {
        // TODO Auto-generated method stub
        return imageIDs.length;
    }

    //returns the ID of an item
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    //returns the ID of an item
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    //returns an ImageView view
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ImageView iv= new ImageView(context);
        iv.setImageResource(imageIDs[position]);
        iv.setScaleType(ImageView.ScaleType.FIT_XY);
        iv.setLayoutParams(new Gallery.LayoutParams(150,120));
        iv.setBackgroundResource(itemBackground);

        return iv;
    }
}}

这里的错误:

04-18 10:38:31.661: D/dalvikvm(10152): Debugger has detached; object registry had 442 entries
04-18 10:38:31.661: D/AndroidRuntime(10152): Shutting down VM
04-18 10:38:31.661: W/dalvikvm(10152): threadid=1: thread exiting with uncaught exception (group=0x4001d820)
04-18 10:38:31.691: E/AndroidRuntime(10152): FATAL EXCEPTION: main
04-18 10:38:31.691: E/AndroidRuntime(10152): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.Bitmap.nativeCreate(Native Method)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.Bitmap.createBitmap(Bitmap.java:499)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.Bitmap.createBitmap(Bitmap.java:466)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:371)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:539)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:508)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:365)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:728)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.content.res.Resources.loadDrawable(Resources.java:1740)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.content.res.Resources.getDrawable(Resources.java:612)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.widget.ImageView.resolveUri(ImageView.java:520)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.widget.ImageView.setImageResource(ImageView.java:305)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at image.view.GalleryView$ImageAdapter.getView(GalleryView.java:95)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.widget.Gallery.makeAndAddView(Gallery.java:776)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.widget.Gallery.fillToGalleryLeft(Gallery.java:695)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.widget.Gallery.trackMotionScroll(Gallery.java:406)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.widget.Gallery$FlingRunnable.run(Gallery.java:1397)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.os.Handler.handleCallback(Handler.java:618)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.os.Handler.dispatchMessage(Handler.java:123)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.os.Looper.loop(Looper.java:154)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at android.app.ActivityThread.main(ActivityThread.java:4668)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at java.lang.reflect.Method.invokeNative(Native Method)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at java.lang.reflect.Method.invoke(Method.java:552)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:917)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674)
04-18 10:38:31.691: E/AndroidRuntime(10152):    at dalvik.system.NativeStart.main(Native Method)
4

8 回答 8

36

为了补充 Ken 的答案,这是一段可靠的代码,我想在他设置好之后我会把它击倒:

    if(imageView != null) {
        ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
    }
    imageView = (ImageView) view.findViewById(R.id.imageView);
    imageView.setImageResource(resID);

注意:如果您尝试交换已回收的图像,这将不起作用。你会在 LOGCAT 中得到类似的东西

画布:尝试使用回收的位图

因此,如果我不必异步加载一堆不同的图像,我现在要做的就是在处理片段和大背景图像时将其放在 onDestroy 中:

@Override
public void onDestroy() {
    super.onDestroy();

    imageView.setImageDrawable(null);
}
于 2013-08-12T22:06:44.527 回答
28

对于那些使用Glide图像加载库的人,他们仍然遇到这些OutOfMemory Exception问题,你可以做很多事情来Glide减少内存的使用,并希望能解决你的问题。这里有几个:

  • 不要android:scaleType="fitXY"在你的ImageView. 所以如果你ImageView看起来像这样:

    <ImageView android:id="@android:id/icon"
           android:layout_width="@dimen/width"
           android:layout_height="@dimen/height"
           android:adjustViewBounds="true" 
           android:scaleType="fitXY" 
          <!-- DON'T USE "fitXY"! -->
    />
    

    更改ImageView以使用不同的android:scaleType,最好是:fitCentercenterCrop

  • 不要在wrap_contentImageView的. _ _ 如果你真的坚持在你的. 中使用,至少设置一个/ 。match_parentwidthheightdpwrap_contentImageViewandroid:maxHeightandroid:maxWidth
  • dontAnimate()根据您的Glide.with()...要求关闭动画:
  • 如果您正在加载大量可能较大的图像(就像在列表/网格中一样),thumbnail(float sizeMultiplier)请在您的请求中指定负载。前任:

    Glide.with(context)
       .load(imageUri)
       .thumbnail(0.5f)
       .dontAnimate()
       .into(iconImageView);
    
  • Glide在您的应用程序的某些阶段使用以下命令暂时降低的内存占用: Glide.get(context).setMemoryCategory(MemoryCategory.LOW).

  • 仅在需要时缓存在内存中,您可以skipMemoryCache(true)根据您的Glide.with()...请求将其关闭:这仍然会将图像缓存到磁盘,您可能会想要它,因为您前面是内存缓存。
  • 如果您Drawable从本地资源加载图像,请确保您尝试加载的图像不是超大的。网上有很多图像压缩工具。这些工具将缩小图像的大小,同时保持其外观质量。
  • 如果从本地资源加载,请使用.diskCacheStrategy(DiskCacheStrategy.NONE).
  • 挂钩到onTrimMemory(int level)Android 提供的回调,以Glide根据需要修剪缓存。前任。

    @Override
    public void onTrimMemory(int level)
    {
        super.onTrimMemory(level);
        Glide.get(this).trimMemory(level);
    }
    
  • 如果在 a 中显示图像,RecyclerView您可以明确清除Glide视图何时被回收,如下所示:

    @Override
    public void onViewRecycled(MyAdapter.MyViewHolder holder)
    {
        super.onViewRecycled(holder);
        Glide.clear(holder.imageView);
    }
    
  • 如果这种情况仍然存在,即使在您“尝试了所有方法”之后,问题可能出在您的应用程序(GASP!),并且Glide只是将其推向OutOfMemory Exception区域的一件事......所以请确保您没有应用程序中的任何内存泄漏。Android Studio提供用于识别应用程序中的内存消耗问题的工具。
  • 最后检查Glide 的 GitHub 上的问题页面,以了解可能提供解决问题的见解的类似问题。回购管理得非常好,他们非常有帮助。
于 2017-05-01T02:23:13.777 回答
27

利用

((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();

在更改为新图像之前!

于 2012-11-16T11:23:09.563 回答
6

图像有各种形状和大小。在许多情况下,它们比典型应用程序用户界面 (UI) 所需的要大。例如,系统图库应用程序显示使用您的 Android 设备的相机拍摄的照片,这些照片的分辨率通常比您设备的屏幕密度高得多。

鉴于您正在使用有限的内存,理想情况下您只想在内存中加载较低分辨率的版本。较低分辨率的版本应该与显示它的 UI 组件的大小相匹配。具有更高分辨率的图像不会提供任何明显的好处,但仍会占用宝贵的内存并由于额外的动态缩放而导致额外的性能开销。

来源:有效加载大型位图

根据上面的信息,我建议您不要像这样设置图像:

setImageResource(resId);

像这样设置它:

setScaledImage(yourImageView, resId);

并复制和粘贴以下方法:

    private void setScaledImage(ImageView imageView, final int resId) {
        final ImageView iv = imageView;
        ViewTreeObserver viewTreeObserver = iv.getViewTreeObserver();
        viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {
                iv.getViewTreeObserver().removeOnPreDrawListener(this);
                int imageViewHeight = iv.getMeasuredHeight();
                int imageViewWidth = iv.getMeasuredWidth();
                iv.setImageBitmap(
                        decodeSampledBitmapFromResource(getResources(),
                                resId, imageViewWidth, imageViewHeight));
                return true;
            }
        });
    }

    private static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                         int reqWidth, int reqHeight) {

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

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

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    private 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;
    }
于 2016-02-16T08:34:47.243 回答
3

您可以将其留给Glide等 3rd 方库

//                imageView.setImageResource(imageId);
                Glide.with(this)  // Activity or Fragment
                        .load(imageId)
                        .into(imageView);

以下是如何将其添加到您的build.gradle

compile group: 'com.github.bumptech.glide', name: 'glide', version: '3.7.0'

Square 的 Picasso 也这样做Picasso 从他们的 URI 加载可绘制资源

于 2016-08-18T22:50:40.743 回答
2

谷歌有正确(完美)的答案:

https://developer.android.com/training/displaying-bitmaps/load-bitmap.html

我如何在片段中使用它的示例:

private ImageView mImageView;
private View view;
private int viewWidth;
private int viewHeight;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    view = inflater.inflate(R.layout.fragment_episode_list, container, false);
    mImageView = (ImageView) view.findViewById(R.id.ImageView);

    ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
    if (viewTreeObserver.isAlive()) {
        viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                viewWidth = view.getMeasuredWidth();
                viewHeight = view.getMeasuredHeight();
                mImageView.setImageBitmap(Methods.decodeSampledBitmapFromResource(getResources(),
                            R.drawable.YourImageName, viewWidth, viewHeight));
            }
        });
    }

    return view;
}

我将这些 Google 方法放入我的“方法”类(放入任何其他有用的方法):

public class Methods {

    ...

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

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                         int reqWidth, int reqHeight) {

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

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

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

}
于 2016-09-11T18:13:16.760 回答
0

@Sakiboy 回答的附加说明。尽管我的回答可能为时已晚,但这是我的解决方案,我发现它无需进行大量代码更改即可工作。

  • 用于Glide处理所有缓存。
  • 要清除更多内存,您应该手动删除所有的views并将任何ImageView位图/可绘制对象设置为null并清除所有事件处理程序和侦听器。
  • 将您在activityor中的所有变量设置fragmentnull.
  • 你需要把你的逻辑放在里面onDestroy,你应该很高兴。
  • 可选步骤是System.gc()在代码末尾添加。

在清除了我之前提到的所有东西之后。您会注意到每次片段/活动被破坏时内存都会下降。

于 2018-04-18T05:00:52.220 回答
-1

当我在横向模式下的 imageview 中显示大图像时,我遇到了同样的问题。所以我用这段代码解决了

            File imgFile = new File(imageFile.getAbsolutePath()); // path of your file

                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(imgFile);
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 8;
                options.inPurgeable = true;
                options.inScaled = true;
                Bitmap bm = BitmapFactory.decodeStream(fis, null,options);
              profileIV.setImageBitmap(bm);
         }
于 2016-03-24T11:44:10.933 回答