title: ViewPager, PagerAdapter and Bitmap cause memory leak (OutOfMemoryError)
body: <p>I have build a Android application which display weather data (I can give you the app name in private if you want to test the problem). The user can browse from one day to other to see weather of a specific day.</p>
  
  <h2>App Architecture</h2>
  
  <p>My application uses fragments (single MainActivity with Navigation Drawer which calls specific fragments).</p>
  
  <p><code>DayPagerFragment</code> uses a <code>ViewPager</code> with an unlimited number of pages (dynamic fragments). A page represent a day.</p>
  
  <p><strong>DayPagerFragment</strong></p>
  
  <pre><code>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() {
              // I don't know the number to put here becauseI don't have 
              // a 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);
      }
  
  }
  </code></pre>
  
  <p>First optimisation: it is managed using a <code>FragmentStatePagerAdapter</code> because <code>FragmentPagerAdapter</code> is not suitable for my use / dynamic fragments (stores the whole Fragments in memory).</p>
  
  <p>Second optmisation: I have set the number of pages that should be retained to either side of the current page with <code>setOffscreenPageLimit(1)</code>.</p>
  
  <p><strong>DayFragment</strong></p>
  
  <pre><code>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
  
          //... get 6 others TextView/ImageView
  
          MyApplication app = (MyApplication) getActivity().getApplicationContext();
  
          // Get 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);
  
          // Get 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);
  
          // Get data from 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 and 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");
      }
  
  }
  </code></pre>
  
  <h2>The problem:</h2>
  
  <p>My application is very graphical because each page displays:</p>
  
  <ul>
  <li>10 TextBox (inner text changes depending on the day)</li>
  <li>4 ImageView (the weather symbol of D-1, D-Day, D+1) + another ImageView</li>
  </ul>
  
  <p>I get OutOfMemoryError rapidly (after +/- 30 pages) when I browse the ViewPager pages.</p>
  
  <p>It is like the Fragments are not being released from memory. The garbage collector does not work like I expected (I think it is because something references the old fragments).</p>
  
  <p><strong>Logcat</strong></p>
  
  <pre><code>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 a 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
              at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
              at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
              at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:422)
              at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:840)
              at android.content.res.Resources.loadDrawable(Resources.java:2110)
              at android.content.res.Resources.getDrawable(Resources.java:700)
              at android.widget.ImageView.resolveUri(ImageView.java:638)
              at android.widget.ImageView.setImageResource(ImageView.java:367)
              at com.example.ui.DayFragment.onCreateView(DayFragment.java:126) //...mLeft.setImageResource()
              at android.support.v4.app.Fragment.performCreateView(Fragment.java:1500)
              at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
              at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
              at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
              at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
              at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
              at android.os.Handler.handleCallback(Handler.java:733)
              at android.os.Handler.dispatchMessage(Handler.java:95)
              at android.os.Looper.loop(Looper.java:136)
              at android.app.ActivityThread.main(ActivityThread.java:5017)
              at java.lang.reflect.Method.invokeNative(Native Method)
              at java.lang.reflect.Method.invoke(Method.java:515)
              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
              at dalvik.system.NativeStart.main(Native Method)
  04-06 20:01:21.773  27008-27008/com.example I/dalvikvm-heap﹕ Clamp target GC heap from 197.480MB to 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
  </code></pre>
  
  <p>I have memory leak but I don't know why and where.
  I have used Eclipse MAT (Memory Analyser) but I don't know where to look.</p>
  
  <p>Can you help me?</p>
  
  <hr>
  
  <p><strong>Edit:</strong> for load Typeface, I use the following code:</p>
  
  <p><strong>DayFragment.java</strong></p>
  
  <pre><code>// Custom fonts
  MyApplication app = (MyApplication) getActivity().getApplication();
  ViewGroup vg = (ViewGroup)getActivity().getWindow().getDecorView();
  ViewUtil.setTypeFace(app.getTrebuchet(), vg);
  </code></pre>
  
  <p><strong>MyApplication.java</strong></p>
  
  <pre><code>public Typeface getTrebuchet() {
      if (trebuchet == null){
          trebuchet = Typeface.createFromAsset(getAssets(), Consts.PATH_TYPEFACE_TREBUCHET);
      }
      return trebuchet;
  }
  </code></pre>
  
  <p>My DDMS show the memory leak:</p>
  
  <p><img src="https://i.stack.imgur.com/mRsdc.png" alt="enter image description here"></p>
  
  <hr>
  
  <p><strong>Edit 2: IMPORTANT!</strong></p>
  
  <p>I use a navigation drawer in my application which is handled by my only activity <code>MainActivity</code>. The navigation drawer uses fragments (and not activities).</p>
  
  <p>This is why <code>DayPagerFragment</code> extends from a <code>Fragment</code> (and not from a <code>FragmentActivity</code> or an <code>Activity</code>).</p>
  
  <p>To swipe between days, the user must touch two buttons (prev/next). I use <code>setOnClickListener</code> on these button in <code>DayFragment</code> (see my updated code).</p>
  
  <p>The problem is that I call <code>((MainActivity)getActivity()).viewDay(calPrev);</code></p>
  
  <p><strong>MainActivity</strong></p>
  
  <pre><code>public class MainActivity extends ActionBarActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks {
  
      private NavigationDrawerFragment mNavigationDrawerFragment;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          ...
          // Set up the navigation drawer
          mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));
      }
      ...
      public void viewDay(Calendar calendar) {
          DayFragment dayFragment = DayFragment.newInstance(calendar); // The problem is here I 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();
      }
  
  }
  </code></pre>
  
  <p>So... I think that because I instanciate each time a new fragment, the <code>ViewPager</code> cannot do his job! And the <code>MainActivity</code> keeps a reference on each Fragments: this is why the garbage collector don't free the memory.</p>
  
  <p>Now:</p>
  
  <ol>
  <li>I don't know if my theory is correct</li>
  <li>How to correct that? How, from <code>setOnClickListener</code> call <code>setCurrentPagerItemPrev</code> and <code>setCurrentPagerItemNext</code> methods (see my updated code in <code>DayPagerFragment</code>)?</li>
  </ol>
  
  <p>NB : I use <code>mAdapterViewPager.getRegisteredFragment()</code> instead of <code>mViewPager.setCurrentItem</code> because my <code>DayAdapter</code> extends from <code>SmartFragmentStatePagerAdapter</code>, but it is the same.</p>