我是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)
答案 0 :(得分:33)
为了补充Ken的答案,这是一段可靠的代码,我认为在他设置之后我会把它击倒:
if(imageView != null) {
((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
}
imageView = (ImageView) view.findViewById(R.id.imageView);
imageView.setImageResource(resID);
注意:如果您尝试交换已经回收的图像,则无法使用此功能。你会在LOGCAT
中得到这样的东西Canvas:尝试使用循环位图
所以,如果我不必异步加载一堆不同的图像,我现在所做的就是在处理片段和大背景图像时简单地将它放在onDestroy中:
@Override
public void onDestroy() {
super.onDestroy();
imageView.setImageDrawable(null);
}
答案 1 :(得分:27)
使用
((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
在换到新形象之前!!
答案 2 :(得分:14)
对于那些使用Glide图片加载库但仍然遇到这些OutOfMemory Exception
问题的人,您可以做很多事情让Glide
使用更少的内存并希望修复你的问题。以下是其中一些:
请勿在{{1}}内使用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
或fitCenter
。
centerCrop
,而是使用wrap_content
或使用{{1}中的尺寸明确指定ImageView
/ match_parent
}。如果确实坚持在width
中使用height
,请至少设置一个dp
/ wrap_content
。ImageView
请求上的android:maxHeight
关闭动画。如果您要加载大量可能较大的图像(就像在列表/网格中一样),请在请求中指定android: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)
。连接到Android提供的Glide.with()...
回调,以根据需要修剪Drawable
缓存。实施例
.diskCacheStrategy(DiskCacheStrategy.NONE)
如果在onTrimMemory(int level)
中显示图片,您可以在回收视图时明确清除Glide
,如下所示:
@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);
}
提供了用于在您的app中识别内存消耗问题的工具。答案 3 :(得分:6)
图像有各种形状和大小。在许多情况下,它们更大 比典型的应用程序用户界面(UI)所需。对于 例如,系统库应用程序显示使用的照片 您的Android设备的相机通常要高得多 分辨率高于设备的屏幕密度。
鉴于你正在使用有限的内存,理想情况下你只想要 在内存中加载较低分辨率的版本。分辨率较低 version应与显示它的UI组件的大小相匹配。一个 具有更高分辨率的图像不会提供任何明显的好处, 但仍占用宝贵的记忆并带来额外的表现 由于额外的飞行缩放而产生的开销。
来源:Loading Large Bitmaps Efficiently
根据以上信息,我建议您不要像这样设置图像:
setImageResource(resId);
将其设置为:
setScaledImage(yourImageView, resId);
和复制&amp;粘贴以下方法:
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;
}
答案 4 :(得分:2)
您可以将其留给第三方库,例如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's Picasso也是Picasso load drawable resources from their URI
答案 5 :(得分:2)
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);
}
}
答案 6 :(得分:0)
@Sakiboy答案的补充说明。 虽然我的回答可能为时已晚,但在我的解决方案中,我发现它无需进行大量的代码更改。
Glide
来处理所有缓存。views
并将任何ImageView
位图/ drawable设置为null
并清除所有事件处理程序和侦听器。 activity
或fragment
中的所有变量设置为null
。 onDestroy
里面,你应该好好去。System.gc()
。清除我之前提到的所有内容。您会注意到每次碎片/活动被破坏时内存都会消失。
答案 7 :(得分:-1)
当我在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);
}