Android OutOfMemoryError:?
Я время от времени получаю OutOfMemoryError: (Heap Size=49187KB, Allocated=41957KB)
в одном из моих приложений. Что я могу сделать, чтобы диагностировать это?
01-09 10:32:02.079: E/dalvikvm(8077): Out of memory: Heap Size=49187KB, Allocated=41957KB, Limit=49152KB
01-09 10:32:02.079: E/dalvikvm(8077): Extra info: Footprint=48611KB, Allowed Footprint=49187KB, Trimmed=7852KB
01-09 10:32:02.079: D/skia(8077): --- decoder->decode returned false
01-09 10:32:02.079: D/AndroidRuntime(8077): Shutting down VM
01-09 10:32:02.079: W/dalvikvm(8077): threadid=1: thread exiting with uncaught exception (group=0x40a97228)
01-09 10:32:02.079: E/AndroidRuntime(8077): FATAL EXCEPTION: main
01-09 10:32:02.079: E/AndroidRuntime(8077): java.lang.OutOfMemoryError: (Heap Size=49187KB, Allocated=41957KB)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:486)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.content.res.Resources.loadDrawable(Resources.java:2044)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.content.res.Resources.getDrawable(Resources.java:675)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.view.View.setBackgroundResource(View.java:11776)
01-09 10:32:02.079: E/AndroidRuntime(8077): at com.blsk.bigtoss.ImageLoader.DisplayImage(ImageLoader.java:81)
01-09 10:32:02.079: E/AndroidRuntime(8077): at com.blsk.bigtoss.MatchActivity$MatchAsyncTask.onPostExecute(MatchActivity.java:1768)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.os.AsyncTask.finish(AsyncTask.java:602)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.os.AsyncTask.access$600(AsyncTask.java:156)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:615)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.os.Handler.dispatchMessage(Handler.java:99)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.os.Looper.loop(Looper.java:156)
01-09 10:32:02.079: E/AndroidRuntime(8077): at android.app.ActivityThread.main(ActivityThread.java:4987)
01-09 10:32:02.079: E/AndroidRuntime(8077): at java.lang.reflect.Method.invokeNative(Native Method)
01-09 10:32:02.079: E/AndroidRuntime(8077): at java.lang.reflect.Method.invoke(Method.java:511)
01-09 10:32:02.079: E/AndroidRuntime(8077): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
01-09 10:32:02.079: E/AndroidRuntime(8077): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
01-09 10:32:02.079: E/AndroidRuntime(8077): at dalvik.system.NativeStart.main(Native Method)
01-09 10:32:02.099: E/EmbeddedLogger(1612): App crashed! Process: com.blsk.bigtoss
01-09 10:32:02.099: E/EmbeddedLogger(1612): App crashed! Package: com.blsk.bigtoss v6 (1.2)
01-09 10:32:02.129: E/EmbeddedLogger(1612): Application Label: Cricket
Это строка, где это происходит:
LinearLayout resultMatchHeaderContainer = new LinearLayout(activity);
if (!resultImagePath.equals("")) {
imageLoader.DisplayImage(resultImagePath,resultMatchHeaderContainer, -1,modifiedHeight, R.drawable.matches_placeholder_result2x);
} else {
try {
resultMatchHeaderContainer.setBackgroundResource(R.drawable.matches_placeholder_result2x);
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
}
7 ответов
Может это тебе поможет?
добавить манифест
Android > v3
<application
....
android:largeHeap="true">
Общие исправления:
1. Исправьте свои контексты:
Попробуйте использовать подходящий контекст: например, так как тост можно увидеть во многих действиях, а не только в одном, используйте getApplicationContext()
для тостов, и так как сервисы могут продолжать работать, даже если действие закончилось, запустите сервис с:
Intent myService = new Intent(getApplicationContext(), MyService.class)
Используйте эту таблицу как краткое руководство для того, какой контекст подходит:
Оригинальная статья о контексте здесь.
2. Проверьте, что вы фактически заканчиваете свои услуги.
Например, у меня есть intentService, который использует API службы определения местоположения Google. И я забыл позвонить googleApiClient.disconnect();
:
//Disconnect from API onDestroy()
if (googleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, GoogleLocationService.this);
googleApiClient.disconnect();
}
3. Проверьте использование изображений и растровых изображений:
Если вы используете библиотеку квадрата Пикассо, я обнаружил, что утечка памяти, не используя .fit()
, что резко сократило объем моей памяти с 50 МБ в среднем до менее чем 19 МБ:
Picasso.with(ActivityExample.this) //Activity context
.load(object.getImageUrl())
.fit() //This avoided the OutOfMemoryError
.centerCrop() //makes image to not stretch
.into(imageView);
4. Если вы используете широковещательные приемники, отмените их регистрацию.
5. Если вы используете java.util.Observer
(Шаблон наблюдателя):
Обязательно используйте deleteObserver(observer);
Вы можете сделать следующее, чтобы избежать этого.
Drawable drawable = resultMatchHeaderContainer.getDrawable();
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if (bitmapDrawable != null) {
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap != null && !bitmap.isRecycled())
bitmap.recycle();
}
}
Загрузка растрового изображения в Imageview всегда была причиной нехватки памяти, это очень распространенная проблема, поэтому мы должны очень осторожно обращаться с изображениями и растровыми изображениями. Что вы можете сделать: при настройке любого фонового растрового изображения для вашего изображения сначала получите прорисовку и переработайте ее так, чтобы она была удалена из памяти, а затем установите новое растровое изображение. Это поможет избежать любой проблемы с OOM. В дальнейшем. Вы можете использовать BitmapFactoryOptions, чтобы уменьшить размер вашего растрового изображения. лайк:
// 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;
FileInputStream stream1 = new FileInputStream(f);
BitmapFactory.decodeStream(stream1, null, o);
stream1.close();
// 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_WIDTH
|| height_tmp / 2 < REQUIRED_HIGHT)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
FileInputStream stream2 = new FileInputStream(f);
Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Это может произойти по нескольким причинам. Возможно, вы слишком долго храните ссылки на другие части кода. Возможно, вы загружаете в большие растровые изображения, которые вместе с удержанием многих ссылок дают вам OOM и т. Д.
Обычно, когда происходит OOM, hprof (снимок кучи) создается в корне sdcard (или во внутреннем хранилище, если sdcard не существует), который может быть прочитан такими инструментами, как Eclipse MAT (включается в инструменты android, если вы используете Затмение). Сначала нужно преобразовать hprof с помощью hprof-conv
инструмент. Вот один урок по использованию Eclipse MAT: исследование использования вашей оперативной памяти. Отчет о подозреваемых утечках является хорошим первым чтением, когда hprof загружен в Eclipse MAT
После профилирования вы можете прочитать о том, как эффективно загружать изображения из " Эффективного отображения растровых изображений".
Есть также несколько популярных библиотек загрузки изображений, таких как универсальный загрузчик изображений и Picasso, которые делают то, что вам нужно с легкостью.
Перед загрузкой изображений в память сожмите их, используя
Bitmap original = BitmapFactory.decodeStream(getAssets().open("1024x768.jpg"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(Bitmap.CompressFormat.PNG, 100, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
Log.e("Original dimensions", original.getWidth()+" "+original.getHeight());
Log.e("Compressed dimensions", decoded.getWidth()+" "+decoded.getHeight());
Если вы получаете растровое изображение из ресурса, в этом случае размер растрового изображения будет зависеть от плотности экрана телефона
Bitmap bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.img_1024x768)).getBitmap();
Log.e("Dimensions", bitmap.getWidth()+" "+bitmap.getHeight());
final Bitmap smile = BitmapFactory.decodeResource(getResources(), R.drawable.emo_im_happy);
Вызов
String pathname=BitMapToString(smile);
а затем позвоните
setImagesNew(linearview,pathname,activity);
...
public String BitMapToString(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
String temp = Base64.encodeToString(b, Base64.DEFAULT);
return temp;
}
public static void setImagesNew(LinearLayout linearLayout, String pathName,
Activity activity) {
Bitmap bmp = decodeSampledBitmapFromResource(pathName,
getDeviceWidth(activity), getDeviceHeight(activity));
linearLayout.setBackgroundDrawable(bmp);
bmp = null;
System.gc();
Runtime.getRuntime().gc();
}
public static Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, options);
}
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;
}
@SuppressLint("NewApi")
public static int getDeviceWidth(Activity activity) {
int deviceWidth = 0;
Point size = new Point();
WindowManager windowManager = activity.getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
windowManager.getDefaultDisplay().getSize(size);
deviceWidth = size.x;
} else {
Display display = windowManager.getDefaultDisplay();
deviceWidth = display.getWidth();
}
return deviceWidth;
}
@SuppressLint("NewApi")
public static int getDeviceHeight(Activity activity) {
int deviceHeight = 0;
Point size = new Point();
WindowManager windowManager = activity.getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
windowManager.getDefaultDisplay().getSize(size);
deviceHeight = size.y;
} else {
Display display = windowManager.getDefaultDisplay();
deviceHeight = display.getHeight();
}
return deviceHeight;
}
пожалуйста, поместите все функции в вашу деятельность и звоните только setImageNew()
и передать параметр в imageview,sdcardpathname и активность
Я надеюсь, что он не потерпит крах после реализации этого кода. потому что у меня возникают такие же проблемы, как у вас..
Я решил ту же проблему:
- уменьшить размер изображения с 5000x2500 до 320x240 - 1080x720 (несколько папок для рисования)
- преобразование изображения PNG в формат WEBP. Его размер уменьшился в 50 раз (с 1,2 МБ до 30 КБ).
Это может произойти, если ресурс растрового изображения расположен неправильно. Лучше прочитать размеры, чтобы увидеть, подходит ли оно памяти. http://developer.android.com/training/displaying-bitmaps/load-bitmap.html