Изначально моя игра потребляет безумный объем памяти около 500 МБ, а затем длится некоторое время примерно 5 раз, а затем падает.Android-игра потребляет безумный объем памяти при начальном запуске игры
Я знаю, что это утечка памяти, но не может определить, поэтому я решил загрузить здесь весь код и обратиться за помощью к сообществу.
Я не использовал никаких фреймворков и не узнал из этого tutorial, это мой проект в колледже. Однако я изменил большую часть кода в соответствии с игрой, которую я хотел сделать.
MyActivity.Java
public class MyActivity extends AppCompatActivity implements
View.OnClickListener {
private AnimationDrawable animationDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
//To portarit
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.rlMy);
ImageButton buttonPlay = (ImageButton) findViewById(R.id.buttonPlay);
ImageButton buttonScore = (ImageButton) findViewById(R.id.buttonScore);
ImageButton buttonInstruction = (ImageButton) findViewById(R.id.buttonInstruction);
ImageButton buttonDeveloper = (ImageButton) findViewById(R.id.buttonDeveloper);
buttonPlay.setOnClickListener(this);
buttonScore.setOnClickListener(this);
buttonInstruction.setOnClickListener(this);
buttonDeveloper.setOnClickListener(this);
relativeLayout.setBackgroundResource(R.drawable.mainscreen);
animationDrawable = (AnimationDrawable) relativeLayout.getBackground();
animationDrawable.start();
}
private void clearData() {
try {
// clearing app data
Runtime runtime = Runtime.getRuntime();
runtime.exec("pm clear com.lud.root.jetfighter;");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.buttonPlay:
startActivity(new Intent(this, GameActivity.class));
finish();
break;
case R.id.buttonScore:
startActivity(new Intent(this, HighScore.class));
finish();
break;
case R.id.buttonDeveloper:
startActivity(new Intent(this, WelcomeActivity.class));
finish();
break;
case R.id.buttonInstruction:
startActivity(new Intent(this, Instruction.class));
finish();
break;
}
}
@Override
public void onBackPressed() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are You Sure you want to exit ?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
GameView.stopMusic();
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startActivity(startMain);
finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
protected void onResume() {
super.onResume();
System.gc();
animationDrawable.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
this.releaseInstance();
unbindDrawables(findViewById(R.id.rlMy));
}
private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
}
GameView.JAVA
public class GameView extends SurfaceView implements Runnable{
volatile boolean playing;
private Thread gameThread = null;
//Adding the player to this class
private Player player;
//Objects used for drawing
private Paint paint;
private Canvas canvas;
private SurfaceHolder surfaceHolder;
//Add stars list
private ArrayList<Star> stars = new ArrayList<Star>();
//Adding Enemies
private Enemy[] enemies; // only one enemy to decrease the difficulty
private int enemyCount = 3; // Number Of Enemies
private Boom boom;
int screenX;
private boolean isGameOver;
int score;
int highScore[] = new int[6];
float distance[] = new float[enemyCount];
public SharedPreferences sharedPreferences;
static MediaPlayer gameOnSound;
final MediaPlayer gameOverSound;
//Context to be used in onTouchEvent on GameOver Screen , for transition from
//GameOver Screen to Main Activity
Context context;
public GameView(Context context, int screenX, int screenY) {
super(context);
player = new Player(context, screenX, screenY);
this.context = context;
//initialize drawing objects
surfaceHolder = getHolder();
paint = new Paint();
int starNums = 50;
for (int i = 0; i < starNums; i++){
Star s = new Star(screenX,screenY);
stars.add(s);
}
enemies = new Enemy[enemyCount];
enemies[0] = new Enemy(context, screenX, screenY); // This needs to be created so that the next enemies
// created can be kept apart and not overlapping
for (int i=1; i<enemyCount; i++)
enemies[i] = new Enemy(context, screenX, screenY, enemies[i-1].getY(),enemies[i-1].getRadius());
boom = new Boom(context);
boom.setX(-250);
boom.setY(-250);
this.screenX = screenX;
isGameOver = false;
score = 0;
sharedPreferences = context.getSharedPreferences("Scores", Context.MODE_PRIVATE);
//initialize the array of high scores
highScore[0] = sharedPreferences.getInt("score1",0);
highScore[1] = sharedPreferences.getInt("score2",0);
highScore[2] = sharedPreferences.getInt("score3",0);
highScore[3] = sharedPreferences.getInt("score4",0);
highScore[4] = sharedPreferences.getInt("score5",0);
gameOnSound = MediaPlayer.create(context,R.raw.gameon);
gameOverSound = MediaPlayer.create(context,R.raw.gameover);
gameOnSound.setLooping(true);
gameOnSound.start();
}
@Override
public void run() {
while (playing){
update();
draw();
control();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_UP:
//stopping the booster when the screen is released
player.stopSlowing();
break;
case MotionEvent.ACTION_DOWN:
//starting the booster when the screen is released
player.startSlowing();
break;
}
if(isGameOver){
if(event.getAction() == MotionEvent.ACTION_DOWN)
context.startActivity(new Intent(context,MyActivity.class));
}
return true;
}
private void update() {
//score based on the time passed
score++;
player.update();
for(Star s : stars)
s.update();
// Below Code is for multiple enemy
for(int i=0; i < enemyCount; i++){
enemies[i].update((int) player.getSpeed() + 10);
distance[i] = (float) Math.sqrt((player.getX()-enemies[i].getX())*(player.getX()-enemies[i].getX()) + (player.getY()-enemies[i].getY())*(player.getY()-enemies[i].getY()));
if(distance[i] < (30 + enemies[i].getRadius())){
boom.setX(player.getX());
boom.setY(player.getY());
player.setX(-200);
isGameOver = true;
playing = false;
gameOnSound.stop();
gameOverSound.start();
highScore[5] = score;
Arrays.sort(highScore);
Log.d("score","Score : "+score+"\nHighScores : "+highScore[0]+"\n"+highScore[1]+"\n"+highScore[2]+"\n"+highScore[3]+"\n"+highScore[4]+"\n"+highScore[5]);
SharedPreferences pref;
pref = context.getSharedPreferences("Scores", Context.MODE_PRIVATE);
SharedPreferences.Editor e = pref.edit();
for(int j=0;j<5;j++){
e.putInt("score"+(j+1),highScore[5-j]);
e.apply();
Log.d("score","score"+(j+1)+"\nHighScores : "+highScore[5-j]);
}
}
}
}
private void draw() {
if(surfaceHolder.getSurface().isValid()){
//lock the canvas
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.BLACK);
//setting the paint color to white to draw the stars
paint.setColor(Color.WHITE);
//drawing all the stars
for (Star s : stars){
paint.setStrokeWidth(s.getStarWidth());
canvas.drawPoint(s.getX(), s.getY(), paint);
}
paint.setTextSize(30);
canvas.drawText("Score : "+score,100,50,paint);
//The following line is for single enemy
paint.setColor(Color.CYAN);
for(int i = 0; i< enemyCount; i++)
canvas.drawCircle(enemies[i].getX(), enemies[i].getY(), enemies[i].getRadius(), paint);
canvas.drawBitmap(boom.getBitmap(),boom.getX(),boom.getY(),paint);
paint.setColor(Color.RED);
canvas.drawCircle(player.getX(), player.getY(), player.getRadius(), paint);
if(isGameOver){
paint.setTextSize(150);
paint.setTextAlign(Paint.Align.CENTER);
int yPos = (int)((canvas.getHeight()/2) - ((paint.descent() + paint.ascent()))/2);
canvas.drawText("GAME OVER",canvas.getWidth()/2, yPos,paint);
}
//Unlocking the canvas
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
private void control() {
try {
gameThread.sleep(15); //creating the frame rate to around 33fps
canvas = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void pause(){
//pausing the game , set the variable to false
playing = false;
try {
gameThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void resume(){
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
public static void stopMusic(){
gameOnSound.stop();
gameOnSound.release();
}
public static void pauseMusic(){
gameOnSound.pause();
}
public static void resumeMusic(){
gameOnSound.start();
}
}
Player.Java
public class Player {
private Bitmap bitmap;
//coordinates
private int x;
private int y;
private float speed ;
private boolean slowing, goingDown;
private int GRAVITY = -10; // For the gravity effect on the ship
//Boundaries
private int maxY;
private int minY;
private int screenY;
private float radius;
private final float MINRADIUS = 30;
private final float MAXRADIUS = 100;
private final int POSGRAVITY = +10;
private final int NEGGRAVITY = -10;
private final int SLOWPOSGRAVITY = +4;
private final int SLOWNEGGRAVITY = -4;
public Player(Context context, int screenX, int screenY){
x = 30; //initial x-position
y = 50; //initial y-position
speed = -4;
radius = MINRADIUS;
maxY = screenY - 30;
this.screenY = screenY;
minY = 30; //equal to initial radius
slowing = false; //initially slowing is false
goingDown = true;
}
public void startSlowing(){
slowing = true;
if(radius > MAXRADIUS)
radius = MAXRADIUS;
else radius += 1.5f;
if(goingDown)
{
speed = -0.3f;
GRAVITY = SLOWNEGGRAVITY;
}
else{
speed = 0.3f;
GRAVITY = SLOWPOSGRAVITY;
}
}
public void stopSlowing(){
slowing = false;
if(goingDown)
{
speed = -4.0f;
GRAVITY = NEGGRAVITY;
}
else{
speed = 4.0f;
GRAVITY = POSGRAVITY;
}
}
public void update(){
minY = (int) getRadius();
maxY = screenY - (int) getRadius();
if(!slowing)
if(radius < MINRADIUS)
radius = MINRADIUS;
else radius -= 0.5f;
if(slowing)
if(radius > MAXRADIUS)
radius = MAXRADIUS;
else radius += 0.5f;
// Moving the ball down
y -= speed + GRAVITY;
if(y < minY)
{
y = minY;
if(slowing)
{
speed = -0.3f;
GRAVITY = SLOWNEGGRAVITY;
}
else
{
speed = -4;
GRAVITY = NEGGRAVITY;
}
goingDown = true;
Log.d("down","Gravity : "+ GRAVITY + " Speed : "+speed+" y : "+y+" goingDown : "+goingDown);
}
if(y > maxY)
{
y = maxY;
if(slowing)
{
speed = 0.3f;
GRAVITY = SLOWPOSGRAVITY;
}
else
{
speed = 4;
GRAVITY = POSGRAVITY;
}
goingDown = false;
Log.d("down","Gravity : "+ GRAVITY + " Speed : "+speed+" y : "+y+" goingDown : "+goingDown);
}
}
public float getSpeed() {
return speed;
}
public int getY() {
return y;
}
public int getX() {
return (int) (getRadius());
}
public void setX(int x) {
this.x = x;
}
public float getRadius() {
return radius;
}
}
Enemy.Java
public class Enemy {
private int x;
private int y;
private int speed = -1;
private int radius = 20;
private int maxX, minX;
private int maxY, minY;
//Create a rect object to detect collision
private Rect detectCollision;
public Enemy(Context context, int screenX, int screenY){
maxX = screenX;
maxY = screenY;
minX = 0;
minY = 0;
// Randomly generating enemy position
Random generator = new Random();
speed = 25;
radius = 35;
x = screenX;
y = generator.nextInt(maxY) + 10 - radius;
}
public Enemy(Context context, int screenX, int screenY, int prevY, int prevRad){
maxX = screenX;
maxY = screenY;
minX = 0;
minY = 0;
// Randomly generating enemy position
Random generator = new Random();
speed = 25;
radius = 35;
x = screenX;
do{
y = generator.nextInt(maxY) + 10 - radius;
}while ((y - prevY) < (prevRad + radius + 60));
}
public void update(int playerSpeed){
// As the enemy moves from right to left
x -= playerSpeed;
x -= speed;
Random generator = new Random();
if(x < minX - this.getRadius()){
speed = generator.nextInt(10) + 10;
radius = generator.nextInt(20) + 30;
x = maxX;
y = generator.nextInt(maxY) - radius;
}
}
//This setter is used for changing the x coordinate after collision
public void setX(int x) {
this.x = x;
}
public Rect getDetectCollision() {
return detectCollision;
}
public int getSpeed() {
return speed;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
public int getRadius() {
return radius;
}
}
Star.Java
public class Star {
private int x;
private int y;
private int speed;
private int maxX;
private int maxY;
private int minX;
private int minY;
public Star(int screenX, int screenY){
maxX = screenX;
maxY = screenY;
minX = 0;
minY = 0;
Random generator = new Random();
speed = generator.nextInt(10);
//generate random coordinate but keeping them inside the screen
x = generator.nextInt(maxX);
y = generator.nextInt(maxY);
}
public void update(){
//To animate the stars on the left side
//Used here is the player's speed
x -= 10;
x -=speed;
if(x < 0){
//Again start the stars from the right edge
//Thus creating an infinite background effect
x = maxX;
Random generator = new Random();
y = generator.nextInt(maxY);
speed = generator.nextInt(15);
}
}
public float getStarWidth(){
//Randomising the star width , for aesthetics
float minX = 1.0f;
float maxX = 4.0f;
Random rand = new Random();
return rand.nextFloat() * (maxX - minX) + minX;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
}
Все эти классы используют только метод холст рисовать, так что я предполагаю, что не должно быть никакой утечки памяти здесь, однако следующий класс Boom, который создает boom.png, когда вы теряете игру, использует растровое изображение, которое не кэшируется, это может быть проблемой, но только небольшая растровая карта не должна потреблять такой безумный объем памяти, я изменил рамку скорость с помощью элемента управления() в классе GameView, но все же ничего хорошего не выходит.
Boom.Java
public class Boom {
private Bitmap bitmap;
private int x,y;
public Boom(Context context){
//get the image from drawable
bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.boom);
//set the coordinate outside the screen so that it won't be
//shown on the screen
//it will be visible for only a fraction of a second
x = -250;
y = -250;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap){
this.bitmap = bitmap;
}
public void setY(int y) {
this.y = y;
}
public void setX(int x) {
this.x = x;
}
}
Я читал о том, как проверить утечку памяти путем отслеживания и профилирование, но это было слишком подавляющим для новичка, как я.
Все эти (DDMS, Android Monitor, Tracing, Profiling), однако я сделал это и обнаружил, что (на основе данных в профилировании приложений в Android Monitor), это то, что я считаю прав, потому что раньше игра была работает только 3 - 4 раза, инструмент показал слишком много, взятые случайной функцией.
Ранее было 3 обращения к случайным в Enemy.java и 1 в Star.java
Я хочу знать причину и устранить то, что мешает моей игре.
Это из моего LogCat: (Я не вижу никаких сообщений dalvikvm, связанные с очисткой)
11-24 16:17:00.844 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.844 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:00.864 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.864 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:00.883 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.884 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:00.903 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.903 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:00.925 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.925 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:00.946 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.947 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:00.968 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.969 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:00.990 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:00.991 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.025 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.027 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.047 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.048 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.052 309 451 I BufferQueueProducer: [SurfaceView](this:0x7f8936e000,id:7877,api:2,p:2347,c:309) queueBuffer: fps=46.72 dur=1005.97 max=36.57 min=19.09
11-24 16:17:01.067 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.067 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$[email protected]
11-24 16:17:01.087 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.088 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.107 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.107 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.126 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.127 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.147 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.147 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.170 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.171 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.192 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.193 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.215 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.215 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.249 2347 2466 I SurfaceView: Locking canvas... stopped=false, [email protected]
11-24 16:17:01.250 2347 2466 I SurfaceView: Returned canvas: [email protected]
11-24 16:17:01.273 2347 2347 D MediaPlayer: handleMessage msg:(8, 0, 0)
11-24 16:17:01.276 2347 2466 D score : Score : 329
11-24 16:17:01.276 2347 2466 D score : HighScores : 248
11-24 16:17:01.276 2347 2466 D score : 269
11-24 16:17:01.276 2347 2466 D score : 317
11-24 16:17:01.276 2347 2466 D score : 329
11-24 16:17:01.276 2347 2466 D score : 460
11-24 16:17:01.276 2347 2466 D score : 579
11-24 16:17:01.282 2347 2466 D score : score1
Этот класс хозяйничает класс Gameview. GameActivity.CLAS
public class GameActivity extends AppCompatActivity{
private GameView gameView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Get the display object
Display display = getWindowManager().getDefaultDisplay();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//Get the screen Resolution
Point size = new Point();
display.getSize(size);
//initialize the gameview object
gameView = new GameView(this, size.x,size.y);
setContentView(gameView);
}
@Override
protected void onPause() {
super.onPause();
gameView.pause();
gameView.pauseMusic();
}
@Override
protected void onResume() {
super.onResume();
System.gc();
gameView.resume();
gameView.resumeMusic();
}
@Override
public void onBackPressed() {
startActivity(new Intent(this, MyActivity.class));
finish();
}
Это выход adb -d shell dumpsys meminfo com.lud.root.jetfighter
команды
Выход памяти во время зависания игры
App Summary
Pss(KB)
------
Java Heap: 3348
Native Heap: 0
Code
: 3320 Stack: 332 Графика: 25175 Частное другое: 11696 Система: 4691
TOTAL: 48562 TOTAL SWAP (KB): 30220
Objects
Views: 35 ViewRootImpl: 3
AppContexts: 5 Activities: 4
Assets: 4 AssetManagers: 2
Local Binders: 11 Proxy Binders: 18
Parcel memory: 3 Parcel count: 14
Death Recipients: 0 OpenSSL Sockets: 0
HPROC Анализ моего изображения. Я не знаю, что сделать вывод, но я полагаю, что это ImageButton вызывает проблему:
Вы должны использовать 'private final Random generator = new Random();' вместо того, чтобы создавать его все время. Это не должно быть так плохо, но когда инструмент жалуется, и средство является тривиальным, то обязательно решите пойти на это. – maaartinus
Я пробовал этот @maaartinus, но все же игра вылетает, однако она не сработала при первом запуске. Спасибо – JDFuzyll
в GameView.java onDraw метод, запишите, сколько из каждого элемента создано, я думаю, что вы делая много растрового изображения, и он будет потреблять много памяти. –