Я сейчас работаю над игрой для Android. Это игра, в которой мне приходится очень часто менять активность. На самом деле это цикл деятельности. После поиска немного в Google я нашел эту статью:
http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html
Одна строка говорит:
«Но для любого другого« нормального »приложения вы все равно можете запустить OOM после некоторого использования. Например, даже когда вы просто открываете / закрываете один и тот же экран (Activity) 20 раз».
Мое приложение требует больше памяти каждый раунд. Но разве нет возможности избежать этой проблемы? Я экономлю много битмапов. Но я не думаю, что это слишком много. Максимальное количество игроков - 8. И каждый игрок получил 2 битмапа. На X10 один будет 120x120, а другой 180x180. Если я сыграю несколько раундов и вернусь на титульный экран, чтобы начать полноценную новую игру, размер кучи может быть уже достаточно полным. Запустив новую игру, я получил возможность сделать снимок с камеры, который я могу использовать в качестве растрового изображения для игроков. Если я достигну OnPictureTaken
, моему приложению понадобится еще 7 МБ на короткое время. Это иногда вызывает MMO в зависимости от того, сколько я играл раньше.
Однако ... вот фрагмент кода, который я пока использую, чтобы избежать OOM.
Перед началом игры я должен сделать несколько настроек. Среди прочего я должен установить количество игроков. Когда я достигаю этого действия, я пытаюсь удалить все растровые изображения созданного мной игрока (которые существуют, если я хочу сыграть в совершенно новую игру после завершения другой)
for (int i = 0; i < StbApp.getPlayer().size(); i++){
StbApp.getPlayer().get(i).getAvaterBig().recycle();
StbApp.getPlayer().get(i).getAvatar().recycle();
StbApp.getPlayer().get(i).setAvatarBig(null);
StbApp.getPlayer().get(i).setAvatar(null);
}
System.gc();
StbApp.getPlayer().clear();
Вот как я делаю фотографию:
Прежде всего параметры:
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
Camera.Size previewSize = previewSizes.get(0);
params.setPreviewSize(previewSize.width, previewSize.height);
params.setPictureFormat(PixelFormat.JPEG);
params.setJpegQuality(50);
camera.setParameters(params);
camera.startPreview();
Это мой метод OnPictureTaken:
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
StbApp.logHeapSizeInfo();
//the name of the file
Log.d(TAG, "GC_ test2");
fileName = String.format("avatar_"+System.currentTimeMillis()+".jpg");
Log.d(TAG, "GC_ test3");
//will save
FileOutputStream fos = openFileOutput(fileName, Context.MODE_PRIVATE);
Log.d(TAG, "GC_ test4");
fos.write(data);
Log.d(TAG, "GC_ test5");
fos.close();
Log.d(TAG, "GC_ test6");
}
catch(Exception e) {
Log.e(TAG, "saveJPEGBitmapToMediaStore: failed to save image", e);
}
}
});
Чем я использую setReult:
if (view == takeAndUseBtn && takeAndUseBtn.getText().toString().equalsIgnoreCase(getResources().getString(R.string.use))) {
if (fileName != null){
Intent intent = new Intent();
intent.putExtra("fileName", fileName);
setResult(RESULT_OK, intent);
this.finish();
} else {
Toast.makeText(this, "wait until picture is saved", Toast.LENGTH_SHORT).show();
Log.d(TAG, "not saved yet");
}
}
И это то, что происходит, если пользователь удовлетворен изображением и хочет использовать его в игре:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "requestCode: " +requestCode);
if (resultCode == RESULT_OK){
String file = data.getExtras().getString("fileName");
FileInputStream fis = null;
try {
fis = openFileInput(file);
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found :(");
e.printStackTrace();
}
if (fis != null){
StbApp.logHeapSizeInfo();
Options opt = new Options();
opt.inSampleSize = 5;
opt.inPurgeable = true;
Bitmap bm = BitmapFactory.decodeStream(fis, null, opt);
bm = Bitmap.createScaledBitmap(bm, getResources().getDrawable(R.drawable.avatar1_big).getMinimumWidth(), getResources().getDrawable(R.drawable.avatar1_big).getMinimumHeight(), false);
StbApp.getPlayer().get(requestCode).setAvatarBig(bm);
bm = Bitmap.createScaledBitmap(bm, getResources().getDrawable(R.drawable.avatar1).getMinimumWidth(), getResources().getDrawable(R.drawable.avatar1).getMinimumHeight(), false);
StbApp.getPlayer().get(requestCode).setAvatar(bm);
bm.setDensity(DisplayMetrics.DENSITY_MEDIUM);
Drawable drawable = new BitmapDrawable(bm);
avatarBtn[requestCode].setBackgroundDrawable(drawable);
bm.setDensity(getResources().getDisplayMetrics().densityDpi);
deleteFile(file);
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
StbApp.logHeapSizeInfo();
}
}
super.onActivityResult(requestCode, resultCode, data);
}
Каждый раз, когда начинается новый раунд, я показываю определенную активность. Я позволил ему запустить экран титров с FLAG_CLEAR_TOP перед тем, как начинать действие, которое я действительно хочу начать, чтобы убедиться, что нет никаких ненужных выполняемых действий:
if (view == startBtn){
StbApp.setRound(StbApp.getRound()+1);
Intent intent = new Intent(this, SpinTheBottle.class);
Intent intentToClear = new Intent(this, TitleScreen.class);
intentToClear.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP + Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intentToClear);
startActivity(intent);
}
Теперь вот несколько журналов из ГХ:
Игра только началась и я еще не сфотографировал:
08-04 08:34:44.355: DEBUG/dalvikvm(317): GC_CONCURRENT freed 1194K, 53% free 3377K/7175K, external 2615K/2699K, paused 1ms+11ms
После того, как я сделаю снимок (до того, как будет вызван OnPictureTaken) (Вот где приложение вылетает, если пользователь играет некоторое время и хочет запустить совершенно новую игру.
08-04 08:36:21.875: DEBUG/dalvikvm(4216): GC_FOR_MALLOC freed 1473K, 60% free 2813K/6983K, external 10938K/12508K, paused 17ms
После того, как я возвращаюсь к предыдущей активности с камеры, чтобы использовать picutre
08-04 08:37:28.365: DEBUG/dalvikvm(317): GC_EXTERNAL_ALLOC freed 221K, 59% free 2992K/7175K, external 2875K/2877K, paused 100ms
Первый раунд начинается:
08-04 08:38:54.005: DEBUG/dalvikvm(4216): GC_EXTERNAL_ALLOC freed 608K, 56% free 3742K/8391K, external 14229K/15604K, paused 28ms
После нескольких раундов (продолжает увеличиваться, пока не падает):
08-04 08:40:07.845: DEBUG/dalvikvm(4216): GC_CONCURRENT freed 1486K, 49% free 4945K/9671K, external 16187K/17938K, paused 2ms+7ms
Иногда, если раунд только начинается, я получаю что-то вроде этого (упражнение, в котором я рисую растровые изображения игроков на холсте с canvas.drawBitmap
08-04 08:40:07.425: DEBUG/dalvikvm(317): GC_CONCURRENT freed 1312K, 54% free 3379K/7303K, external 2139K/2671K, paused 2ms+14ms
Но сразу после этой строки будет строка, подобная той, которую я выложил ранее. Он продолжает расти в конце.
И это то, что происходит, если пользователь хочет начать новую игру и собирается выбрать, сколько игроков в этот раз присоединятся к игре. (Вот где я пытаюсь переопределить растровые изображения и использовать GC вручную)
08-04 08:43:46.635: DEBUG/dalvikvm(4216): GC_EXPLICIT freed 69K, 70% free 3402K/11079K, external 18337K/20004K, paused 31ms
Как видите ... мало что происходит.
Я знаю, что это в значительной степени код. Но я просто не знаю, где я могу попытаться улучшить код, чтобы получить больше памяти. Если кто-нибудь знает хорошую книгу об управлении памятью в Android, я был бы признателен, если бы он / она мог порекомендовать ее мне.