Прорисовка анимации, вызывающая OutOfMemoryError при втором запуске в Android
В настоящее время я занимаюсь разработкой игры и пытаюсь использовать один AnimationDrawable для отображения одной анимации в моем главном меню. Когда я запускаю игру один раз, она отлично работает. Но если я нажму кнопку "Назад", то при повторном открытии игры у меня появится OutOfMemorryError. Если я попадаю домой и возвращаюсь к игре, она загружается, но не показывает анимацию, она пуста, где должна быть.
Я думаю, что когда он снова открывает игру, он пытается загрузить анимацию, но он уже загружен с предыдущего запуска, могу ли я как-нибудь освободить эту память? Как я могу лечить это исключение?
Осматривая, я обнаружил, что это общая проблема в Android, но я не смог найти ничего полезного.
Если это уместно, мои изображения имеют размер 310x316 и у меня есть 114 кадров для загрузки, анимация загружается в формате XML.
Мой класс Main Menu:
public class MainMenuActivity extends Activity{
private String TAG = MainMenuActivity.class.getSimpleName();
ImageView rabbitMenu ;
AnimationDrawable ad;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.mainmenu);
rabbitMenu = (ImageView) findViewById(R.id.rabbitMenu);
rabbitMenu.setImageResource(R.drawable.rabbit_menu_animation);
ad = (AnimationDrawable) rabbitMenu.getDrawable();
}
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
ad.start();
}
}
Журнал ошибок:
01-31 16:13:11.320: E/AndroidRuntime(19550): FATAL EXCEPTION: main
01-31 16:13:11.320: E/AndroidRuntime(19550): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-31 16:13:11.320: E/AndroidRuntime(19550): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
01-31 16:13:11.320: E/AndroidRuntime(19550): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:563)
01-31 16:13:11.320: E/AndroidRuntime(19550): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:439)
Редактировать:
После предложения @OleGG, и теперь второй запуск проходит нормально, но на третьем этапе я, очевидно, пытаюсь использовать переработанное растровое изображение.
Поэтому я отказался от использования AnimationDrawable и использовал AsncTask для решения своей проблемы. Теперь я продолжаю изменять фон ImageView на моем onProgressUpdate(). Возможно, не лучший подход, но сейчас у меня нет проблем! Вот мой код AsyncTask на случай, если у кого-то возникнет такая же проблема!
private class DrawRabbit extends AsyncTask<Void, Integer, Void> {
private boolean running = true;
private int fps = 24;
private int frame = 10014;
private String drawableName;
@Override
protected Void doInBackground(Void... params) {
while (running){
try {
if (frame == 10014)
Thread.sleep(1000);
else
Thread.sleep(fps);
if (frame < 10160) {
frame++;
publishProgress(frame);
}
else
running = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
protected void onProgressUpdate(Integer... frame) {
drawableName = "rm" + frame[0];
int res_id = getResources().getIdentifier(drawableName, "drawable", getPackageName());
rabbitFrame.setBackgroundResource(res_id);
}
}
5 ответов
Вам нужно освободить память, используемую растровыми изображениями, когда вы выходите из этого экрана. К сожалению, для более ранних версий Android (afaik это было "исправлено" начиная с 3.0, но я могу ошибаться) память для растровых изображений выделяется через собственный код, и, что печально, память должна быть явно освобождена.
Итак, я полагаю, вы попробуете этот подход:
- Переместите код для AnimationDrawable в метод onResume().
Добавьте следующий код для освобождения памяти в onPause().
ad.stop(); for (int i = 0; i < ad.getNumberOfFrames(); ++i){ Drawable frame = ad.getFrame(i); if (frame instanceof BitmapDrawable) { ((BitmapDrawable)frame).getBitmap().recycle(); } frame.setCallback(null); } ad.setCallback(null);
Я надеюсь, что это поможет вам.
Итак, мне удается решить мою проблему с помощью этого подхода:
Создал один AsyncTask для рисования
private class DrawRabbit extends AsyncTask<Void, Integer, Void> {
private int fps = 24;
private int frame = 10014;
private String drawableName;
@Override
protected Void doInBackground(Void... params) {
while (running){
try {
if (frame == 10014)
Thread.sleep(1000);
else
Thread.sleep(fps);
if (frame < 10160) {
frame++;
publishProgress(frame);
}
else
running = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
protected void onProgressUpdate(Integer... frame) {
drawableName = "rm" + frame[0];
int res_id = getResources().getIdentifier(drawableName, "drawable", getPackageName());
rabbitFrame.setBackgroundResource(res_id);
}
}
и запустить его на WindowsFocusChanged
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus)
new DrawRabbit().execute((Void)null) ;
else
running = false;
Log.d(TAG,"onFocusChanged");
}
Надеюсь, это поможет кому-то!
Добавьте эти две строки в ваш файл манифеста Android android:hardwareAccelerated="true" и android:largeHeap="true"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxx.yyy" >
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:hardwareAccelerated="true"
android:largeHeap="true">
</application>
Надеюсь, что это поможет вам.
Просто добавьте "android:largeHeap="true"" в тег приложения Android!:)
Вы можете попробовать уменьшить размер изображения. В моем случае это сработало.
int[] frames = {R.drawable.frame1, R.drawable.frame2, R.drawable.frame3};
AnimationDrawable drawable = new AnimationDrawable();
for (int fr : frames) {
Bitmap sample = BitmapFactory.decodeResource(mActivity.getResources(), fr);
sample = Bitmap.createScaledBitmap(sample, sample.getWidth()/4, sample.getHeight()/4, false);
drawable.addFrame(new BitmapDrawable(mActivity.getResources(), sample), 30);
}