奇怪的存储问题,而装载的一个图像要一位图象
-
20-08-2019 - |
题
我有一个列表查看了几个图像上的按钮,每个行。当你点击的清单排,启动一个新的活动。我有建立自己的标签,因为一个问题与机布局。该活动得到启动的结果是地图。如果我点击我的按钮启动的图像预(图片加载关SD卡)的应用程序的返回活动回来的 listview
活动的结果处理程序,以重新启动我的新活动,没有什么比一个图像的部件。
的预览的图像在名单上看是正在做的光标 ListAdapter
.这使得它很简单,但我不知道我怎么可以把一个调整后的图像(I.e。较小比特大小不像素的 src
图像上的按钮。所以我只是调整的图像掉了手机摄像机。
问题是,我得到一个推存的错误,当它试图返回并重新启动2的活动。
- 有没有办法我可以建立的清单适配器很容易地逐行,在那里我可以调整在飞行(位明智的)?
这将是可取的,因为我还需要做出一些改变的性质的部件/元件中的每一个行为我无法选择排的触摸屏幕,因为焦点问题。(我可以使用的滚珠。)
- 我知道我可以做一个出来的波段调整和节省的我的形象,但这不是真的我想要做什么,但是一些样品代码为那将是好的。
只要我无障碍上的图像的清单看它工作得很好。
供参考:这是我这样做:
String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
哪里 R.id.imagefilename
是一个 ButtonImage
.
这是我的传感:
01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed
我也有一个新的时的错误显示图像:
01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri:
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed
解决方案
的 安卓培训 类,"显示高效率地位图",提供一些伟大的信息对于理解和处理异常 java.lang.OutOfMemoryError: bitmap size exceeds VM budget
当装载位图。
读位尺寸和类型
的 BitmapFactory
类提供了几种解码方法(decodeByteArray()
, decodeFile()
, decodeResource()
, 等)。 为创建一个 Bitmap
从各种来源。选择最适当的解码方法的基础上你的图像数据来源。这些方法的尝试来分配存储器的构造位并因此可以很容易地导致一个 OutOfMemory
例外。每种类型的解码方法具有额外的签名指定解码的选择通过 BitmapFactory.Options
类。设置 inJustDecodeBounds
酒店来 true
而解码避免了存分配,返回 null
为bitmap对象而是设置 outWidth
, outHeight
和 outMimeType
.这种技术可以让你读的尺寸和类型的图像数据之前建设(并存分配)的位图。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
为了避免 java.lang.OutOfMemory
例外情况,检查尺寸的一位解码之前,除非您完全信任来源以提供可以预见的是大小的图像数据适合于可用的存储器。
载的按比例缩减版本进入存储器
现在图像的尺寸是已知的,它们可以被用来决定,如果完整的图像应该载入存储器或如果二次取样的版本中应载入的替代。这里有一些要考虑的因素:
- 估计存储器使用的装载完整的图像存储器中。
- 量的存你们愿意致力于载入这种图像所给出的任何其他存储器的要求的应用程序。
- 尺寸的目标的图片视图或用户界面组件,图像是被载入。
- 屏幕上的尺寸、密度的电流的设备。
例如,它不得加载1024×768素的图像进入存储器,如果它最终将显示在一个128x96素的缩略中的一个 ImageView
.
告诉码子样本的图像,载入一个较小的版本进入存储器、设置 inSampleSize
要 true
在你 BitmapFactory.Options
对象。例如,图像分辨率的2048x1536这是解码用 inSampleSize
4产生一位约512x384.装载这个进入存储器使用0.75MB而不是12MB为完整的图像(假定一个位置的 ARGB_8888
).这里的计算方法一样尺寸的价值,是基于两个目标的宽度和高度:
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;
}
注意到:一权力的两个值计算,因为采用解码器 最终的价值按四舍五入至最接近权力的两个,因为每
inSampleSize
文件。
使用这种方法,先解码用 inJustDecodeBounds
设置 true
, 通过选项,通过并随后解码的再次使用新的 inSampleSize
值和 inJustDecodeBounds
设置 false
:
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);
}
这种方法使得它很容易载位的任意大小进一 ImageView
这显示100×100素略,如以下例编码:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
你可以遵循类似的过程来解码位图从其他来源,通过替代合适的 BitmapFactory.decode*
方法需要。
其他提示
修复的内存的错误,你应该做的事情是这样的:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);
此 inSampleSize
选项,可减少存储的消耗。
这里是一个完整的方法。第一,它会读取图像大小没有解码的内容本身。然后找到的最好的 inSampleSize
值,它应该是一个功率为2,最后的图像进行解码。
// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// The new size we want to scale to
final int REQUIRED_SIZE=70;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while(o.outWidth / scale / 2 >= REQUIRED_SIZE &&
o.outHeight / scale / 2 >= REQUIRED_SIZE) {
scale *= 2;
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
我做了一个小小的改进对费奥多尔的代码。它基本上不相同,但没有(我的意见)丑虽然循环,它始终的结果在功的两个。荣誉对费奥多尔为使原来的解决方案,我被卡住直到我找到他,然后我能够做这个:)
private Bitmap decodeFile(File f){
Bitmap b = null;
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
int scale = 1;
if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE /
(double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = new FileInputStream(f);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
return b;
}
我来自iOS的经验和我很沮丧,以发现问题与一些东西所以基本的装载和图像。毕竟,每个人都有这个问题是试图显示合理大小的图像。无论如何,这是两个改变,固定我的问题(和由我的应用程序非常敏感).
1)每一次你做的 BitmapFactory.decodeXYZ()
, 确保通过在一个 BitmapFactory.Options
与 inPurgeable
设置 true
(和最好有 inInputShareable
还设置 true
).
2)从来没有使用 Bitmap.createBitmap(width, height, Config.ARGB_8888)
.我的意思是从来没有!我从来没有过那东西不是提高记忆的错误之后的几个通行证。无量的 recycle()
, System.gc()
, ,任何帮助。它总是会提出的例外。一个其他方式实际工作的是要有一个虚拟的图像在你可绘(或另一位你解码的使用上述步骤1),重新调整,以无论你想要什么,然后操纵该得到的位(例如通过它上一帆布更多的乐趣)。那么,你应该使用什么相反的是: Bitmap.createScaledBitmap(srcBitmap, width, height, false)
.如果由于某种原因你必须使用暴力的创建方法,然后至少通过 Config.ARGB_4444
.
这几乎可以保证救你小时如果不是几天。所有关于扩展的图像,等等。真正地不工作(除非你考虑得到错误的尺寸或退化图像的一个解决方案)。
这是一个 知错误, ,这不是因为大型文件。由于安卓缓存的可绘,这是出去的记忆使用后的几个图像。但我已经找到替代它的方式,跳过安卓默认的高速缓存系统。
解决方案:移动图像的"资产"的文件夹和使用的下列功能,以获得BitmapDrawable:
public static Drawable getAssetImage(Context context, String filename) throws IOException {
AssetManager assets = context.getResources().getAssets();
InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
Bitmap bitmap = BitmapFactory.decodeStream(buffer);
return new BitmapDrawable(context.getResources(), bitmap);
}
我有这个相同的问题和解决它通过避免BitmapFactory.decodeStream或decodeFile职能,而不是用 BitmapFactory.decodeFileDescriptor
decodeFileDescriptor
看起来像它呼吁不同的地方法比decodeStream/decodeFile.
不管怎么说,什么是这样(注意我加入了一些选项,因为一些人以上,但那不是作了区别。什么是关键的是呼叫 BitmapFactory.decodeFileDescriptor 而不是的 decodeStream 或 decodeFile):
private void showImage(String path) {
Log.i("showImage","loading:"+path);
BitmapFactory.Options bfOptions=new BitmapFactory.Options();
bfOptions.inDither=false; //Disable Dithering mode
bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
bfOptions.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
bfOptions.inTempStorage=new byte[32 * 1024];
File file=new File(path);
FileInputStream fs=null;
try {
fs = new FileInputStream(file);
} catch (FileNotFoundException e) {
//TODO do something intelligent
e.printStackTrace();
}
try {
if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
} catch (IOException e) {
//TODO do something intelligent
e.printStackTrace();
} finally{
if(fs!=null) {
try {
fs.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
im.setImageBitmap(bm);
//bm.recycle();
bm=null;
}
我认为是有问题的地函数中使用的decodeStream/decodeFile.我已经证实,一个不同的地方法被称为当使用decodeFileDescriptor.还有什么我读过是"意图像(位图)没有分配标准Java的方式,但是通过当地电话;拨款是外完成的虚拟堆,但是 算了!"
我认为最好的方式,以避免的 OutOfMemoryError
是去面对和理解。
我做了一个 应用程序 故意的原因 OutOfMemoryError
, 和监视存储器的使用。
之后我已经做了大量的实验与这个程序,我已经得到了以下结论:
我要谈谈SDK版本之前亲爱的梳子第一次。
图片储存在本地堆,但它将得到垃圾收集的自动调回收()是不必要的。
如果{VM堆尺寸}+{配的机堆存}>={VM堆大小限制的设备}和你都在试图创建的位图,OOM会被抛出。
注意:VM堆尺寸计算,而不是VM分配的存储器。
VM堆大小不会缩小后的增长,即使所分配的虚拟机内存被收缩.
所以你必须保持高峰VM存尽可能低的水平保持VM堆大小从不断增长的太大了保存可用的存储器,用于位图。
手动电话系统。gc()是毫无意义的,该系统将它称之前,首先试图增长堆大小。
本地堆大小不会缩小,但它不计入OOM,所以不需要担心它。
然后,让我们来谈谈SDK开始从亲爱的梳子。
位是存在虚拟机堆,本机存储器不计算OOM.
本条件OOM是简单得多:{VM堆尺寸}>={VM堆大小限制的设备}.
让你有更多可用的存储器,以创建一位与同一堆大小的限制,OOM是不太可能被抛出。
这里是我的一些观察垃圾收集和存储器泄漏。
你可以看到它自己在应用程序。如果一项活动执行际关,仍在运行之后的活动被摧毁,该活动将不会得到垃圾收集,直到际关完成。
这是因为人际关的一个实例中的匿名内部流,它拥有一个基准的活动。
叫际关.取消(true)将不会停止执行,如果任务是阻止在一个IO作背景线。
回调都是匿名的内部类太多,因此,如果一个静态的实例在项目中拥有他们,不要释放他们,记忆将会被泄露。
如果你计划的一个重复或延迟的任务,例如一个计时器,你不要打电话取消()和清除()在onPause()、存储器将被泄露。
我看到了很多有关的问题OOM例外和缓存的最近。开发指南 一个真正的好文章 在此,但是一些往往无法执行它在适当方式。
因为这是我写的一个例子应用程序,演示了缓存在一个安卓环境。这个执行尚未得到OOM.
看看结尾的这一答案的一个链接到源代码。
要求:
- 安卓API2.1或更高(我只是不能管理到获取可用的存储器中的应用程序API1.6-这是唯一的一段代码不起作用在API1.6)
- 安卓支持包
特点:
- 保留的高速缓冲,如果有一个方向改变, 使用一个单一实例
- 使用 八分之一 所分配的应用程序的记忆缓存(修改如果你想要的)
- 大阵图 得到缩放 (你可以定义素最大允许)
- 控制 有一个互联网连接的 之前下载的位图
- 确保你是唯一的实例, 一个任务 每行
- 如果 你扔 的
ListView
走,它根本不会载的位图之间
这不包括:
- 盘缓存。这应该是很容易实现无论如何-只是点到不同的任务,抓住位图从磁盘
样本代码:
图像正在下载的图像(75x75)从Flickr。但是,把任何网址图像你想要得到处理,并应将它的规模下如果超过了最大值。在这个应用程序的网址是简单地在一个 String
阵列。
的 LruCache
有一个良好的方式来处理位图。然而,在这个应用程序,我投入的一个实例 LruCache
在另一个缓类,我在创建以来获得应用程序更为可行。
Cache.java's重要的东西(在 loadBitmap()
方法是最重要的):
public Cache(int size, int maxWidth, int maxHeight) {
// Into the constructor you add the maximum pixels
// that you want to allow in order to not scale images.
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
mBitmapCache = new LruCache<String, Bitmap>(size) {
protected int sizeOf(String key, Bitmap b) {
// Assuming that one pixel contains four bytes.
return b.getHeight() * b.getWidth() * 4;
}
};
mCurrentTasks = new ArrayList<String>();
}
/**
* Gets a bitmap from cache.
* If it is not in cache, this method will:
*
* 1: check if the bitmap url is currently being processed in the
* BitmapLoaderTask and cancel if it is already in a task (a control to see
* if it's inside the currentTasks list).
*
* 2: check if an internet connection is available and continue if so.
*
* 3: download the bitmap, scale the bitmap if necessary and put it into
* the memory cache.
*
* 4: Remove the bitmap url from the currentTasks list.
*
* 5: Notify the ListAdapter.
*
* @param mainActivity - Reference to activity object, in order to
* call notifyDataSetChanged() on the ListAdapter.
* @param imageKey - The bitmap url (will be the key).
* @param imageView - The ImageView that should get an
* available bitmap or a placeholder image.
* @param isScrolling - If set to true, we skip executing more tasks since
* the user probably has flinged away the view.
*/
public void loadBitmap(MainActivity mainActivity,
String imageKey, ImageView imageView,
boolean isScrolling) {
final Bitmap bitmap = getBitmapFromCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.ic_launcher);
if (!isScrolling && !mCurrentTasks.contains(imageKey) &&
mainActivity.internetIsAvailable()) {
BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
mainActivity.getAdapter());
task.execute();
}
}
}
你不需要修改任何东西Cache.java 文件除非你想要实现的盘缓存。
MainActivity.java's重要的东西:
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (view.getId() == android.R.id.list) {
// Set scrolling to true only if the user has flinged the
// ListView away, hence we skip downloading a series
// of unnecessary bitmaps that the user probably
// just want to skip anyways. If we scroll slowly it
// will still download bitmaps - that means
// that the application won't wait for the user
// to lift its finger off the screen in order to
// download.
if (scrollState == SCROLL_STATE_FLING) {
mIsScrolling = true;
} else {
mIsScrolling = false;
mListAdapter.notifyDataSetChanged();
}
}
}
// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
final ViewHolder holder;
if (row == null) {
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(R.layout.main_listview_row, parent, false);
holder = new ViewHolder(row);
row.setTag(holder);
} else {
holder = (ViewHolder) row.getTag();
}
final Row rowObject = getItem(position);
// Look at the loadBitmap() method description...
holder.mTextView.setText(rowObject.mText);
mCache.loadBitmap(MainActivity.this,
rowObject.mBitmapUrl, holder.mImageView,
mIsScrolling);
return row;
}
getView()
被称为非常频繁。它通常不是一个好主意下载图像存在,如果我们没有实施检查,以确保我们,我们不会开始一个无限量的线每行。Cache.java 检查是否 rowObject.mBitmapUrl
已经在任务和如果是,它不会开始另一个。因此,我们最有可能不超过该工作队的限制,从 AsyncTask
游泳池。
下载:
你可以下载的源代码 https://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip.
最后一句话:
我已经测试了几个星期,现在,我还没有得到一个单一的OOM例外。我已经测试了这个在模拟器上,在我联系一个和我的关系S.我试图像的网址含有图像。唯一的瓶颈是,它需要更多的时间来下载。
只有一种可能的情况,在那里我可以想象,OOM会出现,也就是说,如果我们下载很多的,真的很大影像,并在他们得到扩展和投入缓存,同时将采取更多的存储器和导致OOM.但是,这甚至不是一个理想的情况无论如何,它最有可能不可能解决在一个更加可行的方式。
报告中的错误意见!:-)
我下面要采取的图像和调整它的飞行。希望这可以帮助
Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);
看来,这是一个很长的运行问题,有很多不同的解释。我把建议的两种最常见提出答案在这里,但没有一个这些解决我的问题虚拟机的声称它不能负担得起的字节的执行 解码 这一进程的一部分。之后,一些挖掘我了解到,这里真正的问题是解码过程中考虑离开 本机的 堆。
在这里看到: BitmapFactory OOM把我逼疯的
这导致我到另一个线讨论在那里,我发现一对夫妇更多办法解决这一问题。一个是呼叫System.gc();
手动之后你的图像显示。但是,实际上使得您的应用程序使用更多的存储器,努力减少土堆。更好的解决方案作为释放的2.0(甜甜圈)是使用BitmapFactory选项"inPurgeable".因此我简单地加入 o2.inPurgeable=true;
只是之后 o2.inSampleSize=scale;
.
更多关于该主题: 是的限制的存储器堆只有6米?
现在,具有上述这一切,我是个完全的傻瓜Java和安卓。所以如果你认为这是一个可怕的方式来解决这个问题,你可能是正确的。;-),但这已经创造了奇迹,我们发现它无法运行VM出的堆高速缓存。唯一的缺点我可以找到的是你正在摧毁缓存绘制图像。这意味着,如果你直接回到图像,你是重新划定它的每一时间。在这种情况下如何我应用程序的工作,这不是一个真正的问题。你的里程可能会有所不同。
不幸的是 如果没有上述的工作,那么添加这个给你 清单 文件。内部 应用程序 标记
<application
android:largeHeap="true"
使用这个 bitmap.recycle();
这可以帮助没有任何图像质量问题。
我有一个更有效的解决方案不需要扩展的任何排序。简单地位解码你只有一次,然后缓存在一个地图反对它的名称。然后简单地检索的位反对的名称,并将它设置在图片视图.没有什么更多的工作要做。
这将工作,因为实际的二进制数据的解码位不是储存在安装VM堆。它存储的外部。所以每次你解码位,它分配的存储器之外的VM堆这是从来没有收回通过GC
来帮助你更好的理解这一点,想象一下,你有你的图像绘制文件夹。你刚刚得到的图像做getResources().getDrwable(R.可画.).这不会解你的形象;但是,重新使用已经解码的实例,你每次呼叫。因此,在本质上它是缓存。
现在,由于你的形象是在一个文件的某个地方(或甚至可能来自一个外部服务器),这是你的责任来缓解码的位的实例可以重复使用任何需要的地方。
希望这会有所帮助。
我已经解决了相同的问题,在以下方式。
Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
b.eraseColor(0xFFFFFFFF);
Rect r = new Rect(0, 0,320 , 424);
Canvas c = new Canvas(b);
Paint p = new Paint();
p.setColor(0xFFC0C0C0);
c.drawRect(r, p);
d = mContext.getResources().getDrawable(mImageIds[position]);
d.setBounds(r);
d.draw(c);
/*
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inTempStorage = new byte[128*1024];
b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
o2.inSampleSize=16;
o2.inPurgeable = true;
*/
} catch (Exception e) {
}
i.setImageBitmap(b);
有两个问题。
- 位存储器不是在虚拟机堆,而是在本地堆-看看 BitmapFactory OOM把我逼疯的
- 垃圾收集土堆懒惰于VM堆-所以你需要被非常积极的做位图。回收和位=null每次你去过一个活动的onPause或onDestroy
这个工作对我!
public Bitmap readAssetsBitmap(String filename) throws IOException {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
if(bitmap == null) {
throw new IOException("File cannot be opened: It's value is null");
} else {
return bitmap;
}
} catch (IOException e) {
throw new IOException("File cannot be opened: " + e.getMessage());
}
}
没有答复上述工作对我来说,但我想出了一个可怕丑陋的解决办法,解决该问题。我增加一个非常小的,1×1素像我的项目作为一种资源,并加载到我的图片视图之前呼吁进垃圾回收。我认为这可能是图片视图并不是释放的位,因此GC从来没有把它捡起来。这是丑陋的,但它似乎是工作现在。
if (bitmap != null)
{
bitmap.recycle();
bitmap = null;
}
if (imageView != null)
{
imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();
imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.
伟大的答案在这里,但我想要一个 完全可用的类 为解决这个问题。。所以我做了一个。
这里是我的 BitmapHelper类 这是OutOfMemoryError证明:-)
import java.io.File;
import java.io.FileInputStream;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
public class BitmapHelper
{
//decodes image and scales it to reduce memory consumption
public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
{
try
{
//Decode image size
BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
bitmapSizeOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);
// load image using inSampleSize adapted to required image size
BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
bitmapDecodeOptions.inPurgeable = true;
bitmapDecodeOptions.inDither = !quickAndDirty;
bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;
Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);
// scale bitmap to mathc required size (and keep aspect ratio)
float srcWidth = (float) bitmapDecodeOptions.outWidth;
float srcHeight = (float) bitmapDecodeOptions.outHeight;
float dstWidth = (float) requiredWidth;
float dstHeight = (float) requiredHeight;
float srcAspectRatio = srcWidth / srcHeight;
float dstAspectRatio = dstWidth / dstHeight;
// recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
// (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
// java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
// I do not excatly understand why, but this way it's OK
boolean recycleDecodedBitmap = false;
Bitmap scaledBitmap = decodedBitmap;
if (srcAspectRatio < dstAspectRatio)
{
scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
// will recycle recycleDecodedBitmap
recycleDecodedBitmap = true;
}
else if (srcAspectRatio > dstAspectRatio)
{
scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
recycleDecodedBitmap = true;
}
// crop image to match required image size
int scaledBitmapWidth = scaledBitmap.getWidth();
int scaledBitmapHeight = scaledBitmap.getHeight();
Bitmap croppedBitmap = scaledBitmap;
if (scaledBitmapWidth > requiredWidth)
{
int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
scaledBitmap.recycle();
}
else if (scaledBitmapHeight > requiredHeight)
{
int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
scaledBitmap.recycle();
}
if (recycleDecodedBitmap)
{
decodedBitmap.recycle();
}
decodedBitmap = null;
scaledBitmap = null;
return croppedBitmap;
}
catch (Exception ex)
{
ex.printStackTrace();
}
return null;
}
/**
* compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
*
* @param requiredWidth
* @param requiredHeight
* @param powerOf2
* weither we want a power of 2 sclae or not
* @return
*/
public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
{
int inSampleSize = 1;
// Raw height and width of image
final int srcHeight = options.outHeight;
final int srcWidth = options.outWidth;
if (powerOf2)
{
//Find the correct scale value. It should be the power of 2.
int tmpWidth = srcWidth, tmpHeight = srcHeight;
while (true)
{
if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
break;
tmpWidth /= 2;
tmpHeight /= 2;
inSampleSize *= 2;
}
}
else
{
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap drawableToBitmap(Drawable drawable)
{
if (drawable instanceof BitmapDrawable)
{
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
{
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// RECREATE THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
}
这对我的作品。
Bitmap myBitmap;
BitmapFactory.Options options = new BitmapFactory.Options();
options.InPurgeable = true;
options.OutHeight = 50;
options.OutWidth = 50;
options.InSampleSize = 4;
File imgFile = new File(filepath);
myBitmap = BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);
这是在C#monodroid.你可以轻易改变路径图像。什么这里重要的是选项来加以设定。
这似乎是适当的地方分享我的实用程序类装载和处理的图像与的社会,欢迎使用和修改它自由。
package com.emil;
import java.io.IOException;
import java.io.InputStream;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
* A class to load and process images of various sizes from input streams and file paths.
*
* @author Emil http://stackoverflow.com/users/220710/emil
*
*/
public class ImageProcessing {
public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
if(ImageProcessing.checkDecode(options)){
return bm;
}else{
throw new IOException("Image decoding failed, using stream.");
}
}
public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
if(ImageProcessing.checkDecode(options)){
return bm;
}else{
throw new IOException("Image decoding failed, using file path.");
}
}
public static Dimensions getDimensions(InputStream stream) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
BitmapFactory.decodeStream(stream,null,options);
if(ImageProcessing.checkDecode(options)){
return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
}else{
throw new IOException("Image decoding failed, using stream.");
}
}
public static Dimensions getDimensions(String imgPath) throws IOException{
BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
BitmapFactory.decodeFile(imgPath,options);
if(ImageProcessing.checkDecode(options)){
return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
}else{
throw new IOException("Image decoding failed, using file path.");
}
}
private static boolean checkDecode(BitmapFactory.Options options){
// Did decode work?
if( options.outWidth<0 || options.outHeight<0 ){
return false;
}else{
return true;
}
}
/**
* Creates a Bitmap that is of the minimum dimensions necessary
* @param bm
* @param min
* @return
*/
public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
int newWidth, newHeight;
switch(min.type){
case WIDTH:
if(bm.getWidth()>min.minWidth){
newWidth=min.minWidth;
newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
break;
case HEIGHT:
if(bm.getHeight()>min.minHeight){
newHeight=min.minHeight;
newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
break;
case BOTH: // minimize to the maximum dimension
case MAX:
if(bm.getHeight()>bm.getWidth()){
// Height needs to minimized
min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
if(bm.getHeight()>min.minDim){
newHeight=min.minDim;
newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
}else{
// Width needs to be minimized
min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
if(bm.getWidth()>min.minDim){
newWidth=min.minDim;
newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
}else{
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
}
break;
default:
// No resize
newWidth=bm.getWidth();
newHeight=bm.getHeight();
}
return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
}
public static int getScaledWidth(int height, Bitmap bm){
return (int)(((double)bm.getWidth()/bm.getHeight())*height);
}
public static int getScaledHeight(int width, Bitmap bm){
return (int)(((double)bm.getHeight()/bm.getWidth())*width);
}
/**
* Get the proper sample size to meet minimization restraints
* @param dim
* @param min
* @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
* @return
*/
public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
switch(min.type){
case WIDTH:
return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
case HEIGHT:
return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
case BOTH:
int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
// Return the smaller of the two
if(widthMaxSampleSize<heightMaxSampleSize){
return widthMaxSampleSize;
}else{
return heightMaxSampleSize;
}
case MAX:
// Find the larger dimension and go bases on that
if(dim.width>dim.height){
return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
}else{
return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
}
}
return 1;
}
public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
int add=multipleOf2 ? 2 : 1;
int size=0;
while(min<(dim/(size+add))){
size+=add;
}
size = size==0 ? 1 : size;
return size;
}
public static class Dimensions {
int width;
int height;
public Dimensions(int width, int height) {
super();
this.width = width;
this.height = height;
}
@Override
public String toString() {
return width+" x "+height;
}
}
public static class Minimize {
public enum Type {
WIDTH,HEIGHT,BOTH,MAX
}
Integer minWidth;
Integer minHeight;
Integer minDim;
Type type;
public Minimize(int min, Type type) {
super();
this.type = type;
switch(type){
case WIDTH:
this.minWidth=min;
break;
case HEIGHT:
this.minHeight=min;
break;
case BOTH:
this.minWidth=min;
this.minHeight=min;
break;
case MAX:
this.minDim=min;
break;
}
}
public Minimize(int minWidth, int minHeight) {
super();
this.type=Type.BOTH;
this.minWidth = minWidth;
this.minHeight = minHeight;
}
}
/**
* Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
* @param width
* @param height
* @param config
* @return
*/
public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
long pixels=width*height;
switch(config){
case ALPHA_8: // 1 byte per pixel
return pixels;
case ARGB_4444: // 2 bytes per pixel, but depreciated
return pixels*2;
case ARGB_8888: // 4 bytes per pixel
return pixels*4;
case RGB_565: // 2 bytes per pixel
return pixels*2;
default:
return pixels;
}
}
private static BitmapFactory.Options getOptionsForDimensions(){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds=true;
return options;
}
private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = sampleSize;
options.inScaled = false;
options.inPreferredConfig = bitmapConfig;
return options;
}
}
在我的一个应用程序的我需要画面是从 Camera/Gallery
.如果用户点击图像照相机(可2MP,5MP助8百万像素(或)、图像大小而变化,从 kB
s到 MB
s.如果图像大小小的(或最多1-2MB)上述代码的工作很好,但如果我有图像的大小以上4M或5MB然后 OOM
在框架:(
然后我曾要解决这个问题和最终我做了以下改进对费奥多尔的(所有信贷费奥多尔作出这样的一个很好的解决方案)的代码:)
private Bitmap decodeFile(String fPath) {
// Decode image size
BitmapFactory.Options opts = new BitmapFactory.Options();
/*
* If set to true, the decoder will return null (no bitmap), but the
* out... fields will still be set, allowing the caller to query the
* bitmap without having to allocate the memory for its pixels.
*/
opts.inJustDecodeBounds = true;
opts.inDither = false; // Disable Dithering mode
opts.inPurgeable = true; // Tell to gc that whether it needs free
// memory, the Bitmap can be cleared
opts.inInputShareable = true; // Which kind of reference will be used to
// recover the Bitmap data after being
// clear, when it will be used in the
// future
BitmapFactory.decodeFile(fPath, opts);
// The new size we want to scale to
final int REQUIRED_SIZE = 70;
// Find the correct scale value.
int scale = 1;
if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) opts.outHeight
/ (float) REQUIRED_SIZE);
final int widthRatio = Math.round((float) opts.outWidth
/ (float) REQUIRED_SIZE);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
}
// Decode bitmap with inSampleSize set
opts.inJustDecodeBounds = false;
opts.inSampleSize = scale;
Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(
Bitmap.Config.RGB_565, false);
return bm;
}
我希望这将有助于伙伴面临同样的问题!
对于更多信息,请参阅 此
我刚刚遇到这个问题几分钟前。我解决它做更好的工作,在管理我的列表视图适配器。我认为这是一个问题与数以百计的50x50px图像我用的是,事实证明我是想夸大我自定义视每次行。简单地通过测试,看看如果该行已经膨胀的我消除了这种错误,我是使用数百位图。这实际上是一个微调,但基适配器工作的所有相同的列表视图。这个简单的解决也大大提高的性能的适配器。
@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
if(convertView == null){
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.spinner_row, null);
}
...
我花了一整天测试这些解决方案,唯一的工作对我来说是上述方式获取的图像和手动调用的GC,我知道这是不是应该是必要的,但它是唯一的工作的时候,我把我的应用程序下沉重的负荷测试开关之间的活动。我的程序有一个列表中的缩略图像在一个列表视图在(可以说活动A),当你点击这些图像你的另一项活动(可以说活动B)显示了一个主要的图象,用于该项目。当我会开关之间的来回两项活动,我最终会得到OOM错误和程序,将迫关闭。
当我会得到一半的方式向下列表视图,它将崩溃。
现在,当我实施的以下活动B,我可以通过整个列表视图没有问题,并继续和去...和它的很快。
@Override
public void onDestroy()
{
Cleanup();
super.onDestroy();
}
private void Cleanup()
{
bitmap.recycle();
System.gc();
Runtime.getRuntime().gc();
}
这个问题只会发生在安卓模拟器。我也面临着这个问题在一个仿真程序,但是当我检查的一个的设备,然后它的工作的罚款。
所以请检查设备。它可以运行中的设备。
我2分:我解决了我的OOM错误位图通过:
a)扩展我的图片的一个因素2
b)使用 毕加索 图书馆在我定义的适配器列表视图,有一个呼叫中getView这样的: Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);
使用这些代码为每一个图像中选择显示或drewable转换位的对象。
Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
if (bitmap != null) {
bitmap.recycle();
bitmap = null;
System.gc();
}
bitmap = Bitmap.createScaledBitmap(BitmapFactory
.decodeFile(ImageData_Path.get(img_pos).getPath()),
width, height, true);
} catch (OutOfMemoryError e) {
if (bitmap != null) {
bitmap.recycle();
bitmap = null;
System.gc();
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.RGB_565;
options.inSampleSize = 1;
options.inPurgeable = true;
bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
.getPath().toString(), options), width, height,true);
}
return bitmap;
用你的路径图像instend的 ImageData_Path.获得(img_pos).getPath() .
所有解决方案,这里需要设置一个IMAGE_MAX_SIZE.这限制了设备更强大的硬件和如果图像大小的太低,它看起来丑在高清屏幕上。
我想出了一个解决方案,与我S3和几个其他设备,包括那么强大,有更好的图像质量时更强大的装置是使用。
它的要点是计算的最大存储器分配给应用程序在一个特定的的设备,然后设置的规模,以尽可能低的,不超过这种记忆。这里的代码:
public static Bitmap decodeFile(File f)
{
Bitmap b = null;
try
{
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
try
{
BitmapFactory.decodeStream(fis, null, o);
}
finally
{
fis.close();
}
// In Samsung Galaxy S3, typically max memory is 64mb
// Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
// If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
// We try use 25% memory which equals to 16mb maximum for one bitmap
long maxMemory = Runtime.getRuntime().maxMemory();
int maxMemoryForImage = (int) (maxMemory / 100 * 25);
// Refer to
// http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
// A full screen GridView filled with images on a device with
// 800x480 resolution would use around 1.5MB (800*480*4 bytes)
// When bitmap option's inSampleSize doubled, pixel height and
// weight both reduce in half
int scale = 1;
while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = new FileInputStream(f);
try
{
b = BitmapFactory.decodeStream(fis, null, o2);
}
finally
{
fis.close();
}
}
catch (IOException e)
{
}
return b;
}
我设置的最大存储使用过这位25%的最高分配的存储器,可能需要调整这对你的需求,并确保这位是清除,不留在记忆当时你已经完成之后使用它。通常我用这个代码,以进行图像旋转(资料来源和目的地位),所以我应用程序需要加载2位图在内存在相同的时间,25%给了我一个很好的缓冲器没有运行的存在进行图像旋转。
希望这可以帮助个人在那里..
这样的 OutofMemoryException
不能完全解决通过调用 System.gc()
等等。
通过参照的 活动生命周期
该活动的国家确定的操作系统本身受到存储器的使用为每个进程和优先权的各个过程。
你可以考虑的大小和分辨率为每位图图片的使用。我建议减少尺寸,重新采样,以分辨率较低,请参考的设计画廊(一个小小的图片PNG和一个原始图片。)
一般安卓设备堆大小,仅为16MB(化,从设备/OS见后 堆尺寸),如果你是装的图像和这十字架的尺寸16MB,就会扔出去的记忆除外,而不是使用位于,装载图像从SD卡,或从资源或者甚至是从网络尝试使用 getImageUri ,载入位需要更多的存储器,或者可以设置位于空如果你的工作完成,位图。
这个代码将有助于载大的位图绘制
public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {
Context context;
public BitmapUtilsTask(Context context) {
this.context = context;
}
/**
* Loads a bitmap from the specified url.
*
* @param url The location of the bitmap asset
* @return The bitmap, or null if it could not be loaded
* @throws IOException
* @throws MalformedURLException
*/
public Bitmap getBitmap() throws MalformedURLException, IOException {
// Get the source image's dimensions
int desiredWidth = 1000;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);
int srcWidth = options.outWidth;
int srcHeight = options.outHeight;
// Only scale if the source is big enough. This code is just trying
// to fit a image into a certain width.
if (desiredWidth > srcWidth)
desiredWidth = srcWidth;
// Calculate the correct inSampleSize/scale value. This helps reduce
// memory use. It should be a power of 2
int inSampleSize = 1;
while (srcWidth / 2 > desiredWidth) {
srcWidth /= 2;
srcHeight /= 2;
inSampleSize *= 2;
}
// Decode with inSampleSize
options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = inSampleSize;
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inPurgeable = true;
Bitmap sampledSrcBitmap;
sampledSrcBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);
return sampledSrcBitmap;
}
/**
* The system calls this to perform work in a worker thread and delivers
* it the parameters given to AsyncTask.execute()
*/
@Override
protected Bitmap doInBackground(Object... item) {
try {
return getBitmap();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}