您不能动态增加堆大小,但您可以通过 using 请求使用更多。
android:largeHeap="true"
在manifest.xml 中,您可以在清单中添加在某些情况下有效的这些行。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
您的应用程序的进程是否应该使用大型 Dalvik 堆创建。这适用于为应用程序创建的所有进程。它仅适用于加载到进程中的第一个应用程序;如果您使用共享用户 ID 来允许多个应用程序使用一个进程,则它们都必须一致地使用此选项,否则它们将产生不可预知的结果。
大多数应用程序不应该需要这个,而是应该专注于减少它们的整体内存使用以提高性能。启用此功能也不能保证可用内存的固定增加,因为某些设备受到其总可用内存的限制。
要在运行时查询可用内存大小,请使用方法getMemoryClass() 或getLargeMemoryClass()。
如果仍然遇到问题,那么这也应该有效
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
就显示图像的速度而言,这是对 BitmapFactory.Options.inSampleSize 的最佳使用。
文档提到使用 2 的幂的值,所以我使用 2、4、8、16 等。
让我们更深入地了解图像采样:
例如,如果 1024x768 像素的图像最终会显示在 ImageView 中的 128x128 像素缩略图中,则不值得将其加载到内存中。
要告诉解码器对图像进行二次采样,将较小的版本加载到内存中,请在 BitmapFactory.Options 对象中将 inSampleSize 设置为 true。例如,分辨率为 2100 x 1500 像素的图像使用 4 的inSampleSize 进行解码,会生成大约 512x384 的位图。将其加载到内存中使用 0.75MB 而不是 12MB 的完整图像(假设位图配置为ARGB_8888)。下面是一种基于目标宽度和高度计算样本大小值的方法,该值是 2 的幂:
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;
}
注意:由于解码器使用了
通过四舍五入到最接近的 2 次幂,根据
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);
}
此方法可以轻松地将任意大尺寸的位图加载到显示 100x100 像素缩略图的ImageView 中,如以下示例代码所示:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
您可以按照类似的过程来解码来自其他来源的位图,方法是根据需要替换适当的BitmapFactory.decode* 方法。
我发现这段代码也很有趣:
private Bitmap getBitmap(String path) {
Uri uri = getImageUri(path);
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
in = mContentResolver.openInputStream(uri);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) >
IMAGE_MAX_SIZE) {
scale++;
}
Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ",
orig-height: " + o.outHeight);
Bitmap bitmap = null;
in = mContentResolver.openInputStream(uri);
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
o = new BitmapFactory.Options();
o.inSampleSize = scale;
bitmap = BitmapFactory.decodeStream(in, null, o);
// resize to desired dimensions
int height = bitmap.getHeight();
int width = bitmap.getWidth();
Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
height: " + height);
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x,
(int) y, true);
bitmap.recycle();
bitmap = scaledBitmap;
System.gc();
} else {
bitmap = BitmapFactory.decodeStream(in);
}
in.close();
Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " +
bitmap.getHeight());
return bitmap;
} catch (IOException e) {
Log.e(TAG, e.getMessage(),e);
return null;
}
如何管理应用的内存:link
使用它不是一个好主意android:largeHeap="true"这里是谷歌的摘录解释它,
但是,请求大堆的能力仅适用于
可以证明需要消耗更多 RAM 的一小部分应用程序(例如
作为大型照片编辑应用程序)。永远不要简单地请求大堆
因为你的内存已经用完了,你需要一个快速修复——你应该
仅当您确切知道所有内存在哪里时才使用它
分配以及为什么必须保留它。然而,即使你很自信
您的应用程序可以证明大堆是合理的,您应该避免要求它
任何可能的程度。使用额外的内存将越来越多
不利于整体用户体验,因为垃圾
收集将花费更长的时间并且系统性能可能会变慢
任务切换或执行其他常见操作。
在与out of memory errors 合作后,我会说将其添加到清单中以避免 oom 问题并不是一种罪过
在 Android 运行时 (ART) 上验证应用行为
Android 运行时 (ART) 是运行 Android 5.0(API 级别 21)及更高版本的设备的默认运行时。此运行时提供了许多可提高 Android 平台和应用程序的性能和流畅性的功能。您可以在Introducing ART找到更多关于ART新功能的信息。
但是,一些适用于 Dalvik 的技术不适用于 ART。本文档让您了解在迁移现有应用程序以与 ART 兼容时需要注意的事项。大多数应用程序应该只在使用 ART 运行时才能工作。
解决垃圾收集 (GC) 问题
在 Dalvik 下,应用程序经常发现显式调用 System.gc() 以提示垃圾回收 (GC) 很有用。这对于 ART 来说应该没那么必要了,特别是当您调用垃圾收集来防止 GC_FOR_ALLOC 类型的出现或减少碎片时。您可以通过调用 System.getProperty("java.vm.version") 来验证正在使用的运行时。如果使用 ART,则属性值为“2.0.0”或更高。
此外,Android 开源项目 (AOSP) 正在开发压缩垃圾收集器,以改进内存管理。因此,您应该避免使用与压缩 GC 不兼容的技术(例如保存指向对象实例数据的指针)。这对于使用 Java 本机接口 (JNI) 的应用程序尤其重要。有关详细信息,请参阅防止 JNI 问题。
防止 JNI 问题
ART 的 JNI 比 Dalvik 的要严格一些。使用 CheckJNI 模式来捕获常见问题是一个特别好的主意。如果您的应用使用 C/C++ 代码,您应该查看以下文章:
此外,您可以使用本机内存 (NDK & JNI),因此您实际上绕过了堆大小限制。
这里有一些关于它的帖子:
这是为它制作的库: