android - ViewPager, PagerAdapter and Bitmap cause memory leak (OutOfMemoryError) -


i have build android application display weather data (i can give app name in private if want test problem). user can browse 1 day other see weather of specific day.

app architecture

my application uses fragments (single mainactivity navigation drawer calls specific fragments).

daypagerfragment uses viewpager unlimited number of pages (dynamic fragments). page represent day.

daypagerfragment

public class daypagerfragment extends fragment {      private viewpager mviewpager;      @override     public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) {         return inflater.inflate(r.layout.fragment_day, container, false);     }      @override     public void onviewcreated(view view, bundle savedinstancestate) {         super.onviewcreated(view, savedinstancestate);          mviewpager = (viewpager) view.findviewbyid(r.id.pager);         mviewpager.setoffscreenpagelimit(1);         mviewpager.setadapter(new dayadapter(getchildfragmentmanager()));     }      private static class dayadapter extends fragmentstatepageradapter {          public dayadapter(fragmentmanager fm) {             super(fm);         }          @override         public fragment getitem(int position) {             return dayfragment.newinstance(null);         }          @override         public int getcount() {             // don't know number put here becausei don't have              // defined number of fragments (= dynamic fragments)             return 1;          }          @override         public int getitemposition(object object){             return dayadapter.position_none;         }      }      public void setcurrentpageritemprev() {         //mviewpager.setcurrentitem(mviewpager.getcurrentitem() - 1);         madapterviewpager.getregisteredfragment(mviewpager.getcurrentitem() - 1);     }      public void setcurrentpageritemnext() {         //mviewpager.setcurrentitem(mviewpager.getcurrentitem() + 1);         madapterviewpager.getregisteredfragment(mviewpager.getcurrentitem() + 1);     }  } 

first optimisation: managed using fragmentstatepageradapter because fragmentpageradapter not suitable use / dynamic fragments (stores whole fragments in memory).

second optmisation: have set number of pages should retained either side of current page setoffscreenpagelimit(1).

dayfragment

public class dayfragment extends fragment {      private textview mday;     private textview mmonth;     private button mprevday;     private button mnextday;     private imageview mcenter;     private imageview mleft;     private imageview mright;     ...     private dayrepository dayrepository;     private day currentday;     private day prevday;     private day nextday;     private dayutil dayutil;     private dayutil dayutilprev;     private dayutil dayutilnext;     private calendar cal;     private calendar calprev;     private calendar calnext;      public static dayfragment newinstance(calendar calendar) {         dayfragment dayfragment = new dayfragment();          bundle args = new bundle();         args.putint("year", calendar.get(calendar.year));         args.putint("month", calendar.get(calendar.month));         args.putint("day", calendar.get(calendar.day_of_month));         dayfragment.setarguments(args);          return dayfragment;     }      @override     public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) {         view view = inflater.inflate(r.layout.fragment_day_nested, container, false);          mday = (textview) view.findviewbyid(r.id.textview_day);         mmonth = (textview) view.findviewbyid(r.id.textview_month);         mcenter = (imageview) view.findviewbyid(r.id.imageview_center); // weather symbol (sun, cloud...) of d-day         mleft = (imageview) view.findviewbyid(r.id.imageview_left); // weather symbol of d-1         mright = (imageview) view.findviewbyid(r.id.imageview_right);  // weather symbol of d-2          //... 6 others textview/imageview          myapplication app = (myapplication) getactivity().getapplicationcontext();          // bundle args         int day = getarguments().getint("day");         int month = getarguments().getint("month");         int year = getarguments().getint("year");          // date         this.cal = new gregoriancalendar(year, month, day);          // prev/next day (for nav arrows)         this.calprev = (gregoriancalendar) this.cal.clone();         this.calprev.add(calendar.day_of_year, -1);         this.calnext = (gregoriancalendar) this.cal.clone();         this.calnext.add(calendar.day_of_year, 1);          // data database         //...          // utils         this.dayutil = new dayutil(currentday, getactivity());         this.dayutilprev = new dayutil(this.prevday, getactivity());         this.dayutilnext = new dayutil(this.nextday, getactivity());         string datecurrentdayname = formatutil.getdayname(app.getlocale()).format(this.cal.gettime());         string datecurrentdaynamecap = datecurrentdayname.substring(0,1).touppercase() + datecurrentdayname.substring(1);         string datecurrentmonthname = formatutil.getmonthname(month, app.getlocale());          // update ui         //... lot of settext(...) using day object , utils         mleft.setimageresource(this.dayutilprev.getdrawable());         mcenter.setimageresource(dayutil.getdrawable());         mright.setimageresource(this.dayutilnext.getdrawable());          return view;     }      @override     public void onviewcreated(view view, bundle savedinstancestate) {         super.onviewcreated(view, savedinstancestate);          // custom fonts         myapplication app = (myapplication) getactivity().getapplication();         viewgroup vg = (viewgroup)getactivity().getwindow().getdecorview();         viewutil.settypeface(app.gettrebuchet(), vg);          // navigation between days         mmoonprevday.setonclicklistener(new view.onclicklistener() {             @override             public void onclick(view view) {                  ((mainactivity)getactivity()).viewday(calprev);             }         });         mmoonnextday.setonclicklistener(new view.onclicklistener() {             @override             public void onclick(view view) {                 ((mainactivity) getactivity()).viewday(calnext);             }         });     }      // never called!     @override     public void ondestroy() {         super.ondestroy();         log.w("com.example", "fragment day destroyed");     }  } 

the problem:

my application graphical because each page displays:

  • 10 textbox (inner text changes depending on day)
  • 4 imageview (the weather symbol of d-1, d-day, d+1) + imageview

i outofmemoryerror rapidly (after +/- 30 pages) when browse viewpager pages.

it fragments not being released memory. garbage collector not work expected (i think because references old fragments).

logcat

04-06 20:01:21.683  27008-27008/com.example d/dalvikvm﹕ gc_before_oom freed 348k, 2% free 194444k/196608k, paused 93ms, total 93ms 04-06 20:01:21.683  27008-27008/com.example e/dalvikvm-heap﹕ out of memory on 1790260-byte allocation. 04-06 20:01:21.693  27008-27008/com.example e/androidruntime﹕ fatal exception: main     process: com.example, pid: 27008     java.lang.outofmemoryerror             @ android.graphics.bitmapfactory.nativedecodeasset(native method)             @ android.graphics.bitmapfactory.decodestream(bitmapfactory.java:587)             @ android.graphics.bitmapfactory.decoderesourcestream(bitmapfactory.java:422)             @ android.graphics.drawable.drawable.createfromresourcestream(drawable.java:840)             @ android.content.res.resources.loaddrawable(resources.java:2110)             @ android.content.res.resources.getdrawable(resources.java:700)             @ android.widget.imageview.resolveuri(imageview.java:638)             @ android.widget.imageview.setimageresource(imageview.java:367)             @ com.example.ui.dayfragment.oncreateview(dayfragment.java:126) //...mleft.setimageresource()             @ android.support.v4.app.fragment.performcreateview(fragment.java:1500)             @ android.support.v4.app.fragmentmanagerimpl.movetostate(fragmentmanager.java:927)             @ android.support.v4.app.fragmentmanagerimpl.movetostate(fragmentmanager.java:1104)             @ android.support.v4.app.backstackrecord.run(backstackrecord.java:682)             @ android.support.v4.app.fragmentmanagerimpl.execpendingactions(fragmentmanager.java:1467)             @ android.support.v4.app.fragmentmanagerimpl$1.run(fragmentmanager.java:440)             @ android.os.handler.handlecallback(handler.java:733)             @ android.os.handler.dispatchmessage(handler.java:95)             @ android.os.looper.loop(looper.java:136)             @ android.app.activitythread.main(activitythread.java:5017)             @ java.lang.reflect.method.invokenative(native method)             @ java.lang.reflect.method.invoke(method.java:515)             @ com.android.internal.os.zygoteinit$methodandargscaller.run(zygoteinit.java:779)             @ com.android.internal.os.zygoteinit.main(zygoteinit.java:595)             @ dalvik.system.nativestart.main(native method) 04-06 20:01:21.773  27008-27008/com.example i/dalvikvm-heap﹕ clamp target gc heap 197.480mb 192.000mb 04-06 20:01:21.773  27008-27008/com.example d/dalvikvm﹕ gc_for_alloc freed 565k, 2% free 193932k/196608k, paused 73ms, total 73ms 

i have memory leak don't know why , where. have used eclipse mat (memory analyser) don't know look.

can me?


edit: load typeface, use following code:

dayfragment.java

// custom fonts myapplication app = (myapplication) getactivity().getapplication(); viewgroup vg = (viewgroup)getactivity().getwindow().getdecorview(); viewutil.settypeface(app.gettrebuchet(), vg); 

myapplication.java

public typeface gettrebuchet() {     if (trebuchet == null){         trebuchet = typeface.createfromasset(getassets(), consts.path_typeface_trebuchet);     }     return trebuchet; } 

my ddms show memory leak:

enter image description here


edit 2: important!

i use navigation drawer in application handled activity mainactivity. navigation drawer uses fragments (and not activities).

this why daypagerfragment extends fragment (and not fragmentactivity or activity).

to swipe between days, user must touch 2 buttons (prev/next). use setonclicklistener on these button in dayfragment (see updated code).

the problem call ((mainactivity)getactivity()).viewday(calprev);

mainactivity

public class mainactivity extends actionbaractivity implements navigationdrawerfragment.navigationdrawercallbacks {      private navigationdrawerfragment mnavigationdrawerfragment;      @override     protected void oncreate(bundle savedinstancestate) {         super.oncreate(savedinstancestate);         ...         // set navigation drawer         mnavigationdrawerfragment.setup(r.id.navigation_drawer, (drawerlayout) findviewbyid(r.id.drawer_layout));     }     ...     public void viewday(calendar calendar) {         dayfragment dayfragment = dayfragment.newinstance(calendar); // problem here think !         fragmentmanager fragmentmanager = getsupportfragmentmanager();         fragmentmanager.begintransaction()                 .setcustomanimations(android.r.anim.fade_in, android.r.anim.fade_out)                 .replace(r.id.container, dayfragment)                 .addtobackstack(null)                 .commit();     }  } 

so... think because instanciate each time new fragment, viewpager cannot job! , mainactivity keeps reference on each fragments: why garbage collector don't free memory.

now:

  1. i don't know if theory correct
  2. how correct that? how, setonclicklistener call setcurrentpageritemprev , setcurrentpageritemnext methods (see updated code in daypagerfragment)?

nb : use madapterviewpager.getregisteredfragment() instead of mviewpager.setcurrentitem because dayadapter extends smartfragmentstatepageradapter, same.

you need check 2 things here: images , typefaces.

images uses loads of memory need work release them quickly. i've used following code cleaning memory, removes references helping clean faster.

also, loading many high quality images in short amount of time might cause errors android bit slow grow heap space. see answer to: android understanding heap sizes. might need set android:largeheap="true" in manifest file, after optimising images , fonts.

use ddms heap view check if memory maintaing level, meaning cleaning scrolled pages. requires android tools plugin installed on ide.

public abstract class simplepageradapter extends pageradapter { // ... @override public void destroyitem(viewgroup container, int position, object object) {     container.removeview((view) object);     unbinddrawables((view) object);     object = null; } protected void unbinddrawables(view view) {     if (view.getbackground() != null) {         view.getbackground().setcallback(null);     }     if (view instanceof viewgroup) {         (int = 0; < ((viewgroup) view).getchildcount(); i++) {             unbinddrawables(((viewgroup) view).getchildat(i));         }         ((viewgroup) view).removeallviews();     } } } 

then, check way fonts. make sure cache calls typeface.createfromasset(...), example below:

public typeface getfont(string font, context context) {     typeface typeface = fontmap.get(font);     if (typeface == null) {         typeface = typeface.createfromasset(context.getresources().getassets(), "fonts/" + font);         fontmap.put(font, typeface);     }     return typeface; } 

edit: info after question updated

change adapter use list of objects:

return size of list on getcount(),

when adding objects list call:

notifydatasetchanged(); mpagercontainer.invalidate(); 

then, when getting closer end of current list, request more items , add them list, calling code above again.

i have implemented similar, endless pager loads of hi-res pictures, however, not using fragments, not aware of how removed list.

i can see not override isviewfromobject, perhaps want add following method in pager adapter:

@override public boolean isviewfromobject(view view, object object) {     view _view = (view) object;     return view == _view; } 

Comments

Popular posts from this blog

PHPMotion implementation - URL based videos (Hosted on separate location) -

javascript - Using Windows Media Player as video fallback for video tag -

c# - Unity IoC Lifetime per HttpRequest for UserStore -