内存不足错误ImageView问题
我是新的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)
为了补充肯的答案,这是一个坚实的代码,我想在设置完成之后,
if(imageView != null) { ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle(); } imageView = (ImageView) view.findViewById(R.id.imageView); imageView.setImageResource(resID);
注意:如果您尝试交换已经回收的图像,则这不起作用。 在LOGCAT中你会得到这样的东西
canvas:试图使用回收的位图
所以如果我不需要asynchronous加载一堆不同的图像,我现在要做的就是在处理碎片和大型背景图像时,将其放在onDestroy中:
@Override public void onDestroy() { super.onDestroy(); imageView.setImageDrawable(null); }
使用
((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
在更换新图像之前!
图像有各种形状和大小。 在许多情况下,它们大于典型应用程序用户界面(UI)所需的大小。 例如,系统图库应用程序会显示使用Android设备的相机拍摄的照片,这些照片通常比设备的屏幕密度高得多。
鉴于您正在使用有限的内存,理想情况下,您只需要在内存中加载较低的分辨率版本。 较低的分辨率版本应该与显示它的UI组件的大小相匹配。 具有较高分辨率的图像不会提供任何可见的好处,但仍然占用宝贵的内存并由于额外的即时缩放而导致额外的性能开销。
来源: 高效地加载大的位图
基于上面的信息,我会build议你,而不是像这样设置图像:
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; }
对于那些使用Glide图片加载库的人来说,仍然遇到这些OutOfMemory Exception
问题,可以做很多事情来让Glide
使用更less的内存,并希望能够解决您的问题。 这里有几个:
-
不要在
ImageView
使用android:scaleType="fitXY"
。 所以,如果你是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
,最好是:fitCenter
或centerCrop
。 - 不要在你的
ImageView
使用wrap_content
,而应该使用match_parent
或者使用dp
的尺寸明确指定width
/height
。 如果你真的坚持在你的ImageView
使用wrap_content
,至less要设置一个android:maxHeight
/android:maxWidth
。 - 在您的
Glide.with()...
请求中使用:dontAnimate()
closuresanimation。 -
如果您正在加载大量可能较大的图像(如列表/网格中所示),请在请求中指定一个
thumbnail(float sizeMultiplier)
加载。 例如:Glide.with(context) .load(imageUri) .thumbnail(0.5f) .dontAnimate() .into(iconImageView);
-
通过使用
Glide.get(context).setMemoryCategory(MemoryCategory.LOW)
,在应用程序的某些阶段临时降低Glide
的内存占用。 - 如果需要,只能在内存中caching,您可以在您的
Glide.with()...
请求中使用:skipMemoryCache(true)
将其closures。 这仍然会将图像caching到磁盘,因为您在前面的内存caching,您可能会想要的。 - 如果您正在从本地资源加载
Drawable
,请确保您要加载的图像不是超级巨大的。 网上有大量的图像压缩工具。 这些工具将缩小图像的大小,同时保持其外观质量。 - 如果从本地资源加载使用
.diskCacheStrategy(DiskCacheStrategy.NONE)
。 -
挂钩到Android提供的
onTrimMemory(int level)
callback中,以根据需要修剪Glide
caching。 防爆。@Override public void onTrimMemory(int level) { super.onTrimMemory(level); Glide.get(this).trimMemory(level); }
-
如果在
RecyclerView
显示图像,则可以在视图回收时明确地清除“Glide
,如下所示:@Override public void onViewRecycled(MyAdapter.MyViewHolder holder) { super.onViewRecycled(holder); Glide.clear(holder.imageView); }
- 如果这种情况还在发生,那么即使在“尝试了所有的事情”之后,问题可能就是您的应用程序 (GASP!),而
Glide
只是将它推送到OutOfMemory Exception
区域的一件事情…所以请确保您不要在应用程序中没有任何内存泄漏。Android Studio
提供了用于识别应用中的内存消耗问题的工具。 - 最后,检查Glide的GitHub上的问题页面 ,了解可能提供解决问题的相关问题。 回购pipe理非常好,他们非常有帮助。
Google有正确的答案:
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); } }
您可以将其留给第三方库,如Glide
// 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的毕加索也是这样做毕加索从他们的URI加载drawable资源
当我在LANDSCAPE模式下在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); }