我在我的应用程序中使用自己的Android ViewFlow
示例实现。我正在从Web服务下载加密图像,而不是将它们保存在SD卡上。我正在使用viewflow来动态解密图像并显示它们。但问题是当用户开始更快地更改图像时,它会给我一个OutOfMemoryException
并且我找到/测试的所有信息对我的情况都不起作用。这是我正在使用的:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.image_item, null);
}
try {
File bufferFile = new File(ids.get(position));
FileInputStream fis = new FileInputStream(bufferFile);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
CipherInputStream cis = new CipherInputStream(fis, cipher);
BitmapFactory.Options o = new BitmapFactory.Options();
final int REQUIRED_SIZE=300*1024;
//Find the correct scale value. It should be the power of 2.
int width_tmp= o.outWidth, height_tmp= o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
Bitmap ops = BitmapFactory.decodeStream(cis,null,o2);
((ImageView) convertView.findViewById(R.id.imgView)).setImageBitmap(ops);
cis.close();
fis.close();
System.gc();
} catch (Exception e) {
e.printStackTrace();
((ImageView) convertView.findViewById(R.id.imgView)).setImageResource(R.drawable.image_unavailablee);
}
return convertView;
}
它仍然在线上抛出我的异常:
((ImageView) convertView.findViewById(R.id.imgView)).setImageBitmap(ops);
有这个例外:
java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=6791KB, Allocated=3861KB, Bitmap Size=26006KB)
任何想法如何解决?
答案 0 :(得分:7)
仅供参考处理大位图的人参考,有一篇文章说明如何处理这类问题以避免OutofMemory的最佳方法!
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
希望它有所帮助!
答案 1 :(得分:4)
REQUIRED_SIZE
应包含最大尺寸(宽度,高度,以像素为单位),如
final int REQUIRED_SIZE = 1024; // 1024 pixels wide or long.
在计算缩放系数之前,您还错过了几行将图像边界转换为BitmapFactory.Options o
。
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(cis, null, o);
//The new size we want to scale to
final int REQUIRED_SIZE = 1024;
然后使用o.outWidth
和o.outHeight
来计算比例因子。您可能需要再次获取cis
以进行实际的流解码。
<强>更新强>
此外,您可以将以下变量作为适配器的成员并在构造函数中初始化。
SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
这应该没有问题。
答案 2 :(得分:2)
试试这个:
BitmapFactory.Options o = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];
Bitmap bitmapImage = BitmapFactory.decodeFile(path, options);
而不是:
BitmapFactory.Options o = new BitmapFactory.Options();
final int REQUIRED_SIZE=300*1024;
因此,在使用BitmapFactory.decodeFile()之前,请创建一个16kb的字节数组,并在解码过程中将其传递给临时存储。
希望有所帮助! 引用:Strange out of memory issue while loading an image to a Bitmap object
答案 3 :(得分:2)
我一遍又一遍地遇到同样的问题。
继续我的代码maby它的alitle overkill但是我不得不考虑不同的相机大小,res等原因,但是你必须根据你的需要进行调整。 BitmapFactory.Options imageOptions = new BitmapFactory.Options();
imageOptions.inJustDecodeBounds = true;
ByteArrayInputStream imageByteArrayInputStream = new ByteArrayInputStream(data);
BitmapFactory.decodeStream(imageByteArrayInputStream, null, imageOptions);
System.gc();
// Decode frame size
BitmapFactory.Options frameOptions = new BitmapFactory.Options();
frameOptions.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), selectedFrameResourceID, frameOptions);
System.gc();
// Scale factor for pre scaling
int preScaleFactor = 1;
if (imageOptions.outWidth > frameOptions.outWidth || imageOptions.outHeight > frameOptions.outHeight) {
preScaleFactor = Math.max(imageOptions.outWidth / frameOptions.outWidth, imageOptions.outHeight / frameOptions.outHeight);
}
// Decode with inSampleSize
BitmapFactory.Options scaleOptions = new BitmapFactory.Options();
scaleOptions.inSampleSize = preScaleFactor;
imageByteArrayInputStream = new ByteArrayInputStream(data);
Bitmap preScaledBitmap = BitmapFactory.decodeStream(imageByteArrayInputStream, null, scaleOptions);
System.gc();
Bitmap finalBitmap;
// Scale factor for precise scaling
// If the scaled image is not exactly the same size as the frame than resize it precisely
if (preScaledBitmap.getWidth() != frameOptions.outWidth || preScaledBitmap.getHeight() != frameOptions.outHeight) {
float scaleFactor = Math.max((float)((float)preScaledBitmap.getWidth() / (float)frameOptions.outWidth), (float)((float)preScaledBitmap.getHeight() / (float)frameOptions.outHeight));
float scalePercentage = Math.min((float)((float)frameOptions.outWidth / (float)preScaledBitmap.getWidth()), (float)((float)frameOptions.outHeight / (float)preScaledBitmap.getHeight()));
Matrix matrix = new Matrix();
matrix.preScale(scalePercentage, scalePercentage);
// If the capture width for the source is bigger than the actual width of the source, then set is to the max of the actual source width
int sourceCaptureWidth = (int)(frameOptions.outWidth * scaleFactor);
if (sourceCaptureWidth > preScaledBitmap.getWidth()) {
sourceCaptureWidth = preScaledBitmap.getWidth();
}
// Same as above but than for the height
int sourceCaptureHeight = (int)(frameOptions.outHeight * scaleFactor);
if (sourceCaptureHeight > preScaledBitmap.getHeight()) {
sourceCaptureHeight = preScaledBitmap.getHeight();
}
finalBitmap = Bitmap.createBitmap(preScaledBitmap, 0, 0, sourceCaptureWidth, sourceCaptureHeight, matrix, true);
preScaledBitmap.recycle();
preScaledBitmap = null;
希望这有帮助
答案 4 :(得分:1)
使用recycle()。它将释放与此位图关联的本机对象,并清除对像素数据的引用。
Bitmap ops = BitmapFactory.decodeStream(cis,null,o2);
((ImageView) convertView.findViewById(R.id.imgView)).setImageBitmap(ops);
cis.close();
fis.close();
ops.recycle();
System.gc();
答案 5 :(得分:0)
你在运行什么版本的Android?如果您在Honeycomb或更高版本上运行它,您应该能够使用Eclipse内存分析器来查看内存的使用位置。
话虽这么说,一旦他们不再需要或显示,你需要在你的位图上调用recycle()(这是Sujits回答的问题)。换句话说,如果Bitmap离开屏幕,最好循环()它,然后在它返回视图时再次重新加载它。否则,该位图是usi
要执行此操作,请在ImageView上调用getDrawable(),在ImageView上调用setImageDrawable(null),然后将drawable转换为BitmapDrawable并回收其中的位图。
有关位图内存在Android 3.0之前如何工作的详细信息,您可以看到我在此问题上发布的帖子:http://code.google.com/p/android/issues/detail?id=8488#c80