java - Android memory leak on device, not on emulator -


i'm writing game teach son phonics: it's first attempt @ programming in java, although i've used other languages. game has 4 activities: splash screen initializes array of variables before dismiss it; choose user; third choose level of game play; , fourth play game.

my problem if go in , out of game activity repeatedly, activity crash -- logcat showed oom error. watching heap size did this, , looking @ heap dump mat, looked though leaking whole of fourth activity -- gc not being triggered.

i've tried lots of things track down , fix leak -- of are, i'm sure improvements (e.g. getting rid of non-static inner classes activity) without fixing problem. however, i've tried running same thing on emulator (same target , api device) , there's no leak -- heap size goes , down, gc regularly triggered, doesn't crash.

so going post code activity on here , ask spotting might causing leak, i'm no longer sure that's right question. instead i'm wondering why works on emulator, not phone... have ideas?

ide: android studio 2.1
target: android 6, api 23 (minimum sdk 8)
emulator: android studio
device: sony xperia z2 (now running 6.0.1, had same issue pre recent update, i.e. on api 22)

code activity:

public class gameactivity extends appcompatactivity implements texttospeech.oninitlistener {  //tts object private static texttospeech mytts; //tts status check code private int my_data_check_code = 0; //levelchooser request code public static context gamecontext; private int level; public static string user; private typeface chinacat; public static activity gameactivity = null; private static int[] goldstars = {r.drawable.goldstar1, r.drawable.goldstar2, r.drawable.goldstar3};  @override protected void oncreate(bundle savedinstancestate) {     super.oncreate(savedinstancestate);     requestwindowfeature(window.feature_no_title);     toolbar toolbar = (toolbar) findviewbyid(r.id.toolbar);     setsupportactionbar(toolbar);      gameactivity = this;     gamecontext = this;     level = getintent().getintextra("level", 1);     user = getintent().getstringextra("user");     chinacat = typeface.createfromasset(getassets(), "fonts/chinrg__.ttf");      intent checkttsintent = new intent();     checkttsintent.setaction(texttospeech.engine.action_check_tts_data);     startactivityforresult(checkttsintent, my_data_check_code); }  @override public void onstop() {     if (mytts != null) {         mytts.stop();     }     super.onstop(); }  @override public void ondestroy() {     if (mytts != null) {         mytts.shutdown();     }     button ok_button = (button) findviewbyid(r.id.button);     ok_button.setonclicklistener(null);     imageview tickimageview = (imageview) findviewbyid(r.id.tickimageview);     tickimageview.setonclicklistener(null);      super.ondestroy(); }  protected void onactivityresult(int requestcode, int resultcode, intent data) {     if (requestcode == my_data_check_code) {         if (resultcode == texttospeech.engine.check_voice_data_pass) {             mytts = new texttospeech(this, this);         } else {             intent installttsintent = new intent();             installttsintent.setaction(texttospeech.engine.action_install_tts_data);             startactivity(installttsintent);         }     } }  public void oninit(int initstatus) {     //if tts initialized, load layout , level , assign listeners layout elements     if (initstatus == texttospeech.success) {         mytts.setlanguage(locale.english);          setcontentview(r.layout.activity_main);          imageview imageview = (imageview) findviewbyid(r.id.myimageview);          phonemegroup levelgroup = mainactivity.gamelevel[level]; //set possible words         levelgroup.setsubset(); //randomize subset of possible words actual test         phonicsword[] testset = levelgroup.getsubset(); //fill array of test words          textview[] targetview = new textview[3]; //textviews beginning, middle & end of word         targetview[0] = (textview) findviewbyid(r.id.targetword0);         targetview[1] = (textview) findviewbyid(r.id.targetword1);         targetview[2] = (textview) findviewbyid(r.id.targetword2);          textview[] answersview = new textview[3]; //textviews possible user answer choices         answersview[0] = (textview) findviewbyid(r.id.letter0);         answersview[1] = (textview) findviewbyid(r.id.letter1);         answersview[2] = (textview) findviewbyid(r.id.letter2);          //set first target word, image word, , possible answers         testset[0].setword(levelgroup, targetview, answersview, imageview);         testset[0].speakword(mytts);         //subset index equal array index testset, visible & settable methods         levelgroup.setsubsetindex(0);          for(int i=0; i<3; i++) {             answersview[i].settypeface(chinacat);         }          textview letter0 = (textview) findviewbyid(r.id.letter0);         letter0.setonclicklistener(new letteronclicklistener(testset, levelgroup, targetview, answersview, 0) );         textview letter1 = (textview) findviewbyid(r.id.letter1);         letter1.setonclicklistener(new letteronclicklistener(testset, levelgroup, targetview, answersview, 1) );         textview letter2 = (textview) findviewbyid(r.id.letter2);         letter2.setonclicklistener(new letteronclicklistener(testset, levelgroup, targetview, answersview, 2) );          button ok_button = (button) findviewbyid(r.id.button);         ok_button.setonclicklistener(new okbuttononclicklistener(testset, levelgroup, targetview, level) );          imageview tickimageview = (imageview) findviewbyid(r.id.tickimageview);         tickimageview.setonclicklistener(new tickclick(mytts, testset, levelgroup, targetview, answersview, imageview) );         imageview.setonclicklistener(new wordimageclick(testset, levelgroup) );     }     /*else if todo*/ }  private static class wordimageclick implements view.onclicklistener {     //speaks test word when test image clicked     phonicsword[] testset;     phonemegroup levelgroup;      public wordimageclick(phonicsword[] testset, phonemegroup levelgroup) {         this.testset = testset;         this.levelgroup = levelgroup;     }      @override     public void onclick(view view) {         testset[levelgroup.getsubsetindex()].speakword(mytts);     } }  private static class letteronclicklistener implements view.onclicklistener {     phonemegroup levelgroup;     phonicsword currentword;     phonicsword[] testset;     textview[] targetview;     textview[] answersview;     int item;     int phonemeclicked;      public letteronclicklistener(phonicsword[] testset, phonemegroup levelgroup, textview[] targetview, textview[] answersview, int phonemeclicked) {         this.testset = testset;         this.levelgroup = levelgroup;         this.targetview = targetview;         this.answersview = answersview;         this.phonemeclicked = phonemeclicked;     }      @override     public void onclick(view view) {         this.item = this.levelgroup.getsubsetindex();         this.currentword = this.testset[item];         int = currentword.getomit_index();         targetview[i].settext(answersview[phonemeclicked].gettext());     } }  private void crossclick(view view) {     view.setvisibility(view.invisible);     if(view.gettag()==4){         finish();     } } 

the static variable gameactivity used when you've finished level external class can call gameactivity.gameactivity.finish() after it's displayed how many stars you've got level (it's used call gameactivity.gameactivity.findviewbyid in external class).

public class showstarswithdelay extends handler {  public void handlemessage(message msg) {     imageview starview = (imageview) ((levelendscreens) msg.obj).starview;     imageview highscoreview = (imageview) ((levelendscreens) msg.obj).highscoreview;     int num_currentstars = (int) ((levelendscreens) msg.obj).num_currentstars;     int num_finalstars = (int) ((levelendscreens) msg.obj).num_finalstars;     boolean highscore = (boolean) ((levelendscreens) msg.obj).highscore;     int[] goldstars = (int[])((levelendscreens) msg.obj).goldstars;      if(num_currentstars == num_finalstars) {         if(!highscore) {             starview.setonclicklistener(new view.onclicklistener() {                 @override                 public void onclick(view v) {                     gameactivity.gameactivity.finish();                 }             });         }         else {             highscoreview.setimageresource(r.drawable.highscore);             highscoreview.setvisibility(view.visible);             highscoreview.setonclicklistener(new view.onclicklistener() {                 @override                 public void onclick(view v) {                     gameactivity.gameactivity.finish();                 }             });         }     }     else {         starview.setimageresource(goldstars[num_currentstars++]);         message message = new message();         levelendscreens endscreens = new levelendscreens(starview, highscoreview, num_currentstars, num_finalstars, highscore, goldstars);         message.obj = endscreens;         this.sendmessagedelayed(message, 1000);     } } 

}

in general, want avoid having static reference context anywhere in application (this includes activity classes, of course). reference context may acceptable referencing application context (as there 1 , in memory while app alive anyway).

if need reference calling activity in 1 of children, you'll need pass context parameter, or else use 1 of child views methods retrieve context (such getcontext() views , fragments).

more information should understand memory leaks , why important here: http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html

as example, in code calling finish(), safely change this:

highscoreview.setonclicklistener(new view.onclicklistener() {     @override     public void onclick(view v) {         if (v.getcontext() instanceof activity) {             ((activity)v.getcontext()).finish();         }     } }); 

to sum up, in order fix memory leaks, you'll need remove static keyword of context fields.


Comments

Popular posts from this blog

Export Excel workseet into txt file using vba - (text and numbers with formulas) -

wordpress - (T_ENDFOREACH) php error -

Using django-mptt to get only the categories that have items -