I recently made a game. As I was close to being finished with it, I started running into the OutOfMemory issue when starting new activities. After checking the memory monitor in Android Studio I noticed that my activities stay in memory after I am done with them. How can I free up more memory?
EDIT:
I found another thread where it was mentioned that it may just be the debugger holding dead activities in memory. I tried running my app without debugger, but I can still only open and exit one scoreboard activity and two games before I run out of memory because the activities are still in memory after exiting them. I did a trace and finish()
and onDestroy()
are called.
Currently, according to memory monitor in AS, my MainMenu activity uses about 90 MB and is allocated 100. When I start a game it jumps up to 165 used, then returning from game and starting new game shows 230ish used. Starting more games after that keeps memory usage at 230ish, but if I open a scoreboard it crashes as it needs about 60mb and only has 20mb free and allocating more than 256 mb crashes the app. Same thing happens if I open scoreboard first, then play 2 games. These numbers are higher than I want them to be, and I am looking into ways to reduce ram usage from each activity, but my main concern right now is to free up the ram when an activity is completed.
The programs flow is fairly simple, Activity A is the main menu and it can start activities B and up. With the exception of A the activities can not start a new Activity. There is no communication happening between B and up, all are started by A and returns intents to A.
Since the project is somewhat large, I can't really post all the code, but here's how I start an ScoreboardActivity from A and return the intent from B in my scoreboard class. (Activity is somewhat bloated from all my attempts at removing references)
From Activity A: if user clicks button with ID 3, start scoreboard:
if ( id == 3) {
scoreboardIntent = new Intent(getApplicationContext(), ScoreBoard.class);
startActivityForResult(scoreboardIntent, 0);
}
Here A receives result from Scoreboard. There really isn't a good reason for A to get a result, but I added this to see if I could free memory in the onResult()
callback, all the other Activities do need to return a result though)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data.hasExtra("resultpage")) {
scoreboardIntent = null;
return;
}
Here's the classfile for the Scoreboard:
public class ScoreBoard extends AppCompatActivity implements AdapterView.OnItemClickListener, View.OnClickListener {
ScoreAdapter adapter;
Firebase myFirebase;
DataSnapshot scoreSnapshot;
DataSnapshot noviceSnapshot;
DataSnapshot expertSnapshot;
String difficulty = "novice";
ArrayList<Score> scores = new ArrayList<Score>();
ArrayList<Score> noviceScores = new ArrayList<Score>();
ArrayList<Score> expertScores = new ArrayList<Score>();
ListView listView;
int clicked = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scoreboard_layout);
myFirebase = new Firebase(myFirebaseUri);
getScoresFromFirebase();
listView = (ListView) findViewById(R.id.scoreboardlist);
RadioButton b1 = (RadioButton)findViewById(R.id.button1);
RadioButton b2 = (RadioButton)findViewById(R.id.button2);
b1.setOnClickListener(this);
b2.setOnClickListener(this);
scores = noviceScores;
listView.setOnItemClickListener(this);
adapter = null;
listView.setAdapter(null);
adapter = new ScoreAdapter(this, R.layout.scoreboard_item, scores);
listView.setAdapter(adapter);
adapter.setNotifyOnChange(true);
}
private DataSnapshot getScoresFromFirebase() {
...
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {}
@Override
public void onBackPressed() {
Intent returnIntent = new Intent();
returnIntent.putExtra("resultpage",0);
setResult(Activity.RESULT_OK,returnIntent);
//For the heck of it I decided to null out everything I could
adapter.clear();
adapter = null;
myFirebase = null;
scoreSnapshot = null;
noviceSnapshot = null;
expertSnapshot = null;
difficulty = null;
scores = null;
noviceScores = null;
expertScores = null;
listView = null;
getIntent().setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
finish();
super.finish();
}
@Override
public void onClick(View v) {
if (v.getId() == clicked) return;
clicked = v.getId();
//Show topscores for easy mode
if (v.getId() == R.id.button1) {
scores = noviceScores;
adapter = null;
listView.setAdapter(null);
adapter = new ScoreAdapter(this, R.layout.scoreboard_item, scores);
listView.setAdapter(adapter);
Animation anim = AnimationUtils.loadAnimation(
this, android.R.anim.slide_in_left
);
anim.setDuration(350);
listView.startAnimation(anim);
}
//show topscores for hard mode
if (v.getId() == R.id.button2) {
scores = expertScores;
adapter = null;
listView.setAdapter(null);
adapter = new ScoreAdapter(this, R.layout.scoreboard_item, scores);
listView.setAdapter(adapter);
}
}
}