在加载位图时,我需要计算出可以在没有OutOfMemoryError
的情况下分配的大量内存。我决定从文件中加载真正的大图像。以下是其网址:https://upload.wikimedia.org/wikipedia/commons/3/3d/LARGE_elevation.jpg
图像大小为14 488 498字节(磁盘上为14,5 MB)。这是我的代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String LOG_TAG = "LargeBitmapLargeBitmap";
private ActivityManager mActivityManager;
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
mImageView = (ImageView) findViewById(R.id.image);
findViewById(R.id.get_mem_info).setOnClickListener(this);
findViewById(R.id.load_bitmap).setOnClickListener(this);
}
@Override
public void onClick(final View v) {
switch (v.getId()) {
case R.id.get_mem_info:
Runtime rt = Runtime.getRuntime();
Log.v(LOG_TAG, "maxMemory: " + rt.maxMemory());
Log.v(LOG_TAG, "freeMemory: " + rt.freeMemory());
Log.v(LOG_TAG, "totalMemory: " + rt.totalMemory());
Log.v(LOG_TAG, "mActivityManager.getMemoryClass(): " + mActivityManager.getMemoryClass());
break;
case R.id.load_bitmap:
new ImageLoadingTask(mImageView).execute();
break;
}
}
// I don't handle the screen rotation here since it's a test app
private static class ImageLoadingTask extends AsyncTask<Void, Void, Bitmap> {
private ImageView mImageView;
ImageLoadingTask(ImageView imageView) {
mImageView = imageView;
}
@Override
protected Bitmap doInBackground(final Void... params) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator
+ "LARGE_elevation.jpg";
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError error) {
Log.e(LOG_TAG, "Failed to load the large bitmap", error);
}
return bitmap;
}
@Override
protected void onPostExecute(final Bitmap bitmap) {
super.onPostExecute(bitmap);
mImageView.setImageBitmap(bitmap);
}
}
}
我编译了上面的代码并在我的Nexus 5上运行了。然后我按下了get_mem_info
按钮并获得了以下输出:
05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: maxMemory: 201326592
05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: freeMemory: 4991056
05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: totalMemory: 20733248
05-02 12:07:35.873 28053-28053/com.sample V/LargeBitmapLargeBitmap: mActivityManager.getMemoryClass(): 192
这意味着我的应用程序可用的堆内存为192 MB。但是当我按下load_bitmap
按钮时,图像没有加载,我得到了OutOfMemoryError
:
05-02 12:07:38.712 28053-29533/com.sample E/LargeBitmapLargeBitmap: Failed to load the large bitmap
java.lang.OutOfMemoryError: Failed to allocate a 233280012 byte allocation with 10567360 free bytes and 176MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611)
at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:391)
at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:417)
at com.sample.MainActivity$ImageLoadingTask.doInBackground(MainActivity.java:70)
at com.sample.MainActivity$ImageLoadingTask.doInBackground(MainActivity.java:55)
为什么会这样?为什么系统需要分配233280012字节(约220MB)?部分“10567360免费字节和176MB直到OOM”对我来说也很奇怪。为什么我会看到这些数字?
答案 0 :(得分:3)
首先,第一件事,
14.5 MB是 JPEG 的大小,但不是实际图像的大小
同样地,尝试将图像重新保存为png,您将看到该大小增加了10倍,即它甚至可能达到150 MB
您必须记住的一件事是,这些图像压缩为JPEG或PNG。
但是当这些图像加载到imageview左右时,图像的每个位都会被解压缩并占用RAM中的内存。
因此,图像的实际尺寸基本上是其分辨率乘以4(A-R-G-B)
编辑 - 如何处理这些图像?
首先,使用Glide(或Picasso)加载图像,而不是为此编写自己的AsyncTask。
在Glide中有一种名为override(int width, int height)
的方法,可在下载时调整图像大小。
理想情况下,宽度和高度应该是ImageView(或任何视图)的精确尺寸,这将防止图像像素化,并且还将节省额外的内存消耗。 (您可以随时进行小型计算以保持图像宽高比)
答案 1 :(得分:-2)
您应该使用Glide。它是由Google支持的图书馆。它会自动处理所有内存管理以及图像加载功能。