title: Android ImageSwitcher: Out Of Memory Error when setImageURI
body: <p>I seem to have a memory leak and I'm not sure how to fix.  I've read all of the ImageSwitcher tutorials and examples on android.com, but those all seem to deal with drawables that are already in the drawables folder.  My code allows the user to take one or two photos with their camera, store the images to SD, and use an imageswitcher to "flip" the images over.</p>
  
  <pre><code>public class CardViewImageActivity extends Activity implements ViewFactory {
  
      private CardDBAdapter mDbHelper;
  
      private String _cardImgGuidFront;
      private String _cardImgGuidBack;
      private Boolean frontShowing = false;
      private Boolean hasFront = false;
      private Boolean hasBack = false;
  
      private Uri uriFront;
      private Uri uriBack;
  
      private int cardId;
  
      private ImageSwitcher iSwitcher;
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.card_view_image);
  
          iSwitcher = (ImageSwitcher)findViewById(R.id.imageSwitcher);
          iSwitcher.setFactory(this);
          iSwitcher.setOnClickListener(SwitcherOnClick);
  
          this.cardId = Integer.parseInt(getIntent().getExtras().getString(
                  "cardId")); //$NON-NLS-1$
  
          getCardImageGuids(this.cardId);
  
          if(_cardImgGuidFront != null)
          {
              hasFront = true;
              uriFront = Uri.parse(Environment.getExternalStorageDirectory().toString() + "/" + _cardImgGuidFront + ".jpg");
          }
  
          if(_cardImgGuidBack != null)
          {
              hasBack = true;
              uriBack = Uri.parse(Environment.getExternalStorageDirectory().toString() + "/" + _cardImgGuidBack + ".jpg");            
          }
          if(hasFront &amp;&amp; hasBack)
              Toast.makeText(this, R.string.card_view_touch, Toast.LENGTH_SHORT).show();
  
          if(hasFront)
          {
              iSwitcher.setImageURI(uriFront);
              frontShowing = true;
          }
          else if(hasBack)
          {
              iSwitcher.setImageURI(uriBack);
              frontShowing = false;
          }
          else
          {
              Toast.makeText(this, R.string.card_no_image, Toast.LENGTH_SHORT).show();
          }
      }
      @Override
      public void onDestroy()
      {
          iSwitcher.setImageURI(null);
          super.onDestroy();
      }
  
      public View makeView() {
          ImageView iView = new ImageView(this);
          iView.setScaleType(ImageView.ScaleType.FIT_CENTER);
          iView.setLayoutParams(new ImageSwitcher.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
  
          return iView;
      }
  
  
      protected OnClickListener SwitcherOnClick = new OnClickListener()
      {
          @Override
          public void onClick(View v)
          {
              if(frontShowing &amp;&amp; hasBack)
              {
                  iSwitcher.destroyDrawingCache();
                  iSwitcher.setImageURI(uriBack);
                  frontShowing = false;
              }
              else if(!frontShowing &amp;&amp; hasFront)
              {
                  iSwitcher.destroyDrawingCache();
                  iSwitcher.setImageURI(uriFront);
                  frontShowing = true;
              }
              else
              {
  
              }
          }       
      };
  
  
      private void getCardImageGuids(int cardId)
      {
          try
          {
              this.mDbHelper = new CardDBAdapter(this);
              this.mDbHelper.open();
              Cursor c = this.mDbHelper.fetchCard(this.cardId);
  
  
              _cardImgGuidFront = c.getString(c
                      .getColumnIndex(CardDBAdapter.CARD_IMG_GUID_FRONT));
  
              _cardImgGuidBack = c.getString(c
                      .getColumnIndex(CardDBAdapter.CARD_IMG_GUID_BACK));
          }
          catch(SQLiteException ex)
          {
              Toast.makeText(CardViewImageActivity.this, ex.toString(), Toast.LENGTH_LONG).show();
          }
          finally
          {
               this.mDbHelper.close();
               this.mDbHelper = null;
          }
  
      }
  }
  </code></pre>
  
  <p>The above code seems to work sometimes quite well.  However, I'm occasionally getting an OutOfMemory error.  Now, from my understanding through debugging, makeView() seems to be getting called twice when .setFactory(this) is called and this seems to be fine since ImageSwitcher's purpose is to switch between two images.  I'm wondering if there is a better way to switch the images besides SetImageUri().  I don't see anywhere I might be leaking or what might be causing the issue.  I don't see anywhere that convertView might even be utilized.  Is there a way to cache the images from the Uri?  Are the images being reloaded each time .setImageUri() is called?  Is there a way to dump that memory(or reuse)?  Is this what's eating up my memory?</p>
  
  <p>Not to sound disrespectful or rude, but I would really prefer help without having someone referencing the <strong><em>Avoiding Memory Leaks</em></strong> article or links to the javadocs for imageswitcher?  The <strong><em>Avoid Memory Leaks</em></strong> article shows a couple of "you shouldn't do this" but never shows what you "should" do instead.   I already have links to the javadocs.  I'm looking for someone that can actually explain what I'm doing incorrectly and point me in a better direction with code(I learn best seeing code rather than vague abstract academic theories) rather than just regurgitating the top 3 links from a Google search. :)</p>
  
  <p>Thank you for any help! :)</p>
  
  <p>EDIT:10Feb2012
  So I tried to load the Drawables and IMMEDIATELY received an out of memory error which is WORSE than getting the error occasionally with .setImageUri().  The following contains the mods:</p>
  
  <pre><code>private Drawable front;
  private Drawable back;
  
  @Override
      public void onCreate(Bundle savedInstanceState) {
  
  ...
          Resources res = getResources();
  
  ...
          if(_cardImgGuidFront != null)
          {
              hasFront = true;
              String frontPath = Environment.getExternalStorageDirectory().toString() + "/" + _cardImgGuidFront + ".jpg";
              front = new BitmapDrawable(res, frontPath);
          }
  
          if(_cardImgGuidBack != null)
          {
              hasBack = true;
              String backPath = Environment.getExternalStorageDirectory().toString() + "/" + _cardImgGuidBack + ".jpg";
              back = new BitmapDrawable(res, backPath);
          }
  </code></pre>
  
  <p>Looking at the SoftReference, usage requires the creation of the SoftReference using another drawable.  I don't see how using SoftReference could possibly help since I am now crashing on initial load.</p>