2012-02-10 4 views
1

메모리 누수가있는 것 같아 수정 방법이 확실하지 않습니다. android.com에서 ImageSwitcher 튜토리얼과 예제를 모두 읽었지만,이 모든 것들은 이미 드로어 블 폴더에있는 드로어 블을 처리하는 것처럼 보입니다. 내 코드를 통해 사용자는 카메라로 한 두 장의 사진을 찍어 이미지를 SD에 저장하고 imageswitcher를 사용하여 이미지를 뒤집을 수 있습니다.Android ImageSwitcher : Out of Memory setImageURI 오류

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 && 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 && hasBack) 
      { 
       iSwitcher.destroyDrawingCache(); 
       iSwitcher.setImageURI(uriBack); 
       frontShowing = false; 
      } 
      else if(!frontShowing && 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; 
     } 

    } 
} 

위의 코드는 때로는 꽤 잘 작동하는 것처럼 보입니다. 그러나 때때로 OutOfMemory 오류가 발생합니다. 자, 내 이해에서 디버깅을 통해 makeView()는 두 번 호출되고있는 것 같습니다 .setFactory (this)가 호출되고 ImageSwitcher의 목적이 두 개의 이미지를 전환하는 것이기 때문에 문제가없는 것 같습니다. SetImageUri() 외에도 이미지를 전환하는 더 좋은 방법이 있는지 궁금하네요. 내가 누출되거나 문제를 일으킬 수있는 곳이 어디에도 보이지 않습니다. 나는 convertView가 활용 될지도 모르는 곳을 보지 않는다. Uri에서 이미지를 캐싱 할 수있는 방법이 있습니까? .setImageUri()가 호출 될 때마다 이미지가 다시로드됩니까? 그 메모리를 덤프 (또는 재사용) 할 수있는 방법이 있습니까? 이게 내 기억을 먹어 치우는거야?

는 무례하거나 무례 소리하지 않기,하지만 난 정말 방지 메모리를 참조하는 사람이 imageswitcher에 대한 JavaDoc을 기사 또는 링크를 누출하지 않고 도움을 선호? 메모리 누출 피하기 기사에는 "이 작업을해서는 안되는"몇 가지 내용이 나와 있지만 대신 "수행해야 할 작업"이 나와 있습니다. 이미 javadocs에 대한 링크가 있습니다. 나는 실제로 내가 잘못하고있는 것을 실제로 설명 할 수있는 사람을 찾고 있으며, Google 검색에서 상위 3 개 링크를 역 겨레하기보다는 (코드 명이 모호한 추상 학술 이론보다는 코드를 가장 잘 배웁니다) 코드로 더 나은 방향으로 나를 안내해줍니다. . :)

도움 주셔서 감사합니다! :)

EDIT : 10Feb2012 그래서 Drawables를로드 하려다가 .setImageUri()를 사용하여 때때로 오류를 얻는 것보다 메모리 부족 오류가 즉시 발생했습니다. 다음은 개조가 포함

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); 
     } 

가하는 SoftReference를보고, 사용은 다른 드로어 블을 사용하는 SoftReference의 생성을 필요로한다. SoftReference를 사용하면 초기로드가 충돌하고있는 상황에서 어떻게 도움이 될지 모르겠다.

답변

0

좋아,이게 작동하는 것 같아요. 그리고 나는이 일을 당황한 다른 사람들을 위해 자바 좌절감을 저장하는 코드를 제공 할 것입니다. 아마 가장 예쁜 것은 아니지만, 지금까지 (나무를 노크해라) 나는 더 이상의 메모리 부족을 보지 못했다.

import android.app.Activity; 
import android.content.res.Resources; 
import android.database.Cursor; 
import android.database.sqlite.SQLiteException; 
import android.graphics.BitmapFactory; 
import android.graphics.drawable.BitmapDrawable; 
import android.graphics.drawable.Drawable; 
import android.os.Bundle; 
import android.os.Environment; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.view.ViewGroup.LayoutParams; 
import android.widget.ImageSwitcher; 
import android.widget.ImageView; 
import android.widget.Toast; 
import android.widget.ViewSwitcher.ViewFactory; 

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 Drawable front; 
    private Drawable back; 

    private int cardId; 

    private ImageSwitcher iSwitcher; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.cert_view_image); 

     iSwitcher = (ImageSwitcher)findViewById(R.id.imageSwitcher); 
     iSwitcher.setFactory(this); 
     iSwitcher.setOnClickListener(SwitcherOnClick); 
     Resources res = getResources(); 
     BitmapFactory.Options o = new BitmapFactory.Options(); 
     o.inSampleSize = 2; 

     this.cardId = Integer.parseInt(getIntent().getExtras().getString(
       "cardId")); //$NON-NLS-1$ 

     getCardImageGuids(this.cardId); 

     if(_cardImgGuidFront != null) 
     { 
      hasFront = true; 
      String frontPath = Environment.getExternalStorageDirectory().toString() + "/" + _cardImgGuidFront + ".jpg"; 
      front = new BitmapDrawable(res, BitmapFactory.decodeFile(frontPath, o)); 
     } 

     if(_cardImgGuidBack != null) 
     { 
      hasBack = true; 
      String backPath = Environment.getExternalStorageDirectory().toString() + "/" + _cardImgGuidBack + ".jpg"; 
      back = new BitmapDrawable(res, BitmapFactory.decodeFile(backPath, o)); 
     } 
     if(hasFront && hasBack) 
      Toast.makeText(this, R.string.card_view_touch, Toast.LENGTH_SHORT).show(); 

     if(hasFront) 
     { 
      iSwitcher.setImageDrawable(front); 
      frontShowing = true; 
     } 
     else if(hasBack) 
     { 
      iSwitcher.setImageDrawable(back); 
      frontShowing = false;  } 
     else 
     { 
      Toast.makeText(this, R.string.card_no_image, Toast.LENGTH_SHORT).show(); 
     } 
     res = null; 
    } 
    @Override 
    public void onPause() 
    { 
     super.onPause(); 
    } 
    @Override 
    public void onDestroy() 
    { 
     front = null; 
     back = 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 && hasBack) 
      { 
       iSwitcher.setImageDrawable(back); 
       frontShowing = false; 
      } 
      else if(!frontShowing && hasFront) 
      { 
       iSwitcher.setImageDrawable(front); 
       frontShowing = true; 
      } 
      else 
      { 

      } 
     }  
    }; 


    private void getCardImageGuids(int cardId) 
    { 
     ... 
     // Put your db logic retrieval for the img id here 

    } 
} 

이 솔루션 (및 실제 코드)이 도움이되기를 바랍니다.

0

SetImageUri() 외에도 이미지를 전환하는 더 좋은 방법이 있는지 궁금합니다.

캐시 된 BitmapDrawable을 사용하여 setImageDrawable()을 호출합니다.

어디에도 누출이 없거나 문제의 원인이 될 수 있습니다.

누수 위치를 확인하려면 DDMS and MAT을 사용하십시오.

convertView를 활용할 수있는 곳은 어디에도 없습니다.

소스 코드에 convertView이라는 이름이 지정되지 않았다는 것을 고려하면 이는 놀라운 일이 아닙니다.

Uri에서 이미지를 캐시 할 수있는 방법이 있습니까?

예. BitmapFactory을 사용하여 직접 이미지를로드하십시오. 가능하면 SoftReferences을 사용하여 결과를 캐시하십시오. Bitmap 개체에서 더 이상 필요하지 않은 경우 recycle()으로 전화하십시오.

.setImageUri()를 호출 할 때마다 이미지가 다시로드됩니까?

예.

메모리를 덤프 할 수 있습니까 (또는 재사용 할 수 있습니까)?

Android가없는 경우 Bitmap을 만듭니다. ImageSwitcher은 단방향 API로 보이지만 이미지를 설정할 수는 있지만 검색 할 수는 없습니다.

+0

답장을 보내 주셔서 감사합니다. 네가 옳아. 위의 convertView가 없지만,이 경우 convertView (재생 된 뷰)가 어떻게 사용되는지조차도 알 수 없다고 말하고있었습니다. 나는 메모리에서 비트 맵을 유지하는 것이 잠재적으로 메모리를 많이 차지한다는 것을 읽었다. BitmapFactory에 샷을 제공하고, 특히 WeakReference/SoftReference를 사용하여 도움이되는지 확인합니다. 건배! – Shawn

2
ImageView v = (ImageView)imageSwitcher.getNextView(); 
BitmapDrawable bd = (BitmapDrawable) v.getDrawable(); 
if (bd != null) 
{ 
    Bitmap b = bd.getBitmap(); 
    b.recycle(); 
}