2012-02-10 2 views
1

У меня, кажется, есть утечка памяти, и я не уверен, как исправить. Я прочитал все обучающие материалы и примеры ImageSwitcher на android.com, но все они, похоже, имеют дело с чертежами, которые уже находятся в папке drawables. Мой код позволяет пользователю делать одну или две фотографии с помощью своей камеры, сохранять изображения на SD и использовать обратный переключатель для «переворачивания» изображений.Android ImageSwitcher: вне памяти Ошибка при установкеImageURI

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 (это), и это кажется прекрасным, поскольку целью ImageSwitcher является переход между двумя изображениями. Мне интересно, есть ли лучший способ переключить изображения, кроме SetImageUri(). Я не вижу, чтобы я мог просачиваться или что может вызвать проблему. Я не вижу нигде, что convertView может даже использоваться. Есть ли способ кэшировать изображения из Uri? Вызывается перезагрузка изображений каждый раз .setImageUri()? Есть ли способ сбросить эту память (или повторное использование)? Это то, что питает мою память?

Не звучать неуважительно или грубо, но я действительно предпочел бы помощь, не имея кого-то ссылки на Избежание памяти Утечки статьи или ссылки на Javadocs для imageswitcher? Избегайте утечек памяти статья показывает пару «вы не должны этого делать», но никогда не показывает, что вы «должны» делать. У меня уже есть ссылки на javadocs. Я ищу кого-то, кто может фактически объяснить, что я делаю неправильно, и указать мне в лучшем направлении с кодом (я лучше всего изучаю код, а не расплывчатые абстрактные академические теории), а не просто срывать верхние 3 ссылки из поиска Google , :)

Благодарим за помощь! :)

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

Итак, это похоже на работу, и я предоставлю код, чтобы сохранить разочарование в Java для кого-либо еще, взволнованного этим. Вероятно, это не самый красивый, но до сих пор (стук дерева) я больше не видел ошибок в памяти.

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 

    } 
} 

Я надеюсь, что это решение (и код ACTUAL) поможет кому-то еще.

0

Мне интересно, есть ли лучший способ переключить изображения, кроме SetImageUri().

Call setImageDrawable() с использованием кэшированного BitmapDrawable.

Я не вижу нигде, где бы я ни находился, и что может вызвать проблему.

Используйте DDMS and MAT, чтобы узнать, где находятся ваши утечки.

Я не вижу нигде, что convertView может даже использоваться.

Учитывая, что в вашем исходном коде ничего не указано convertView, это неудивительно.

Есть ли способ кэшировать изображения из Uri?

Да. Используйте BitmapFactory, загрузите изображения самостоятельно. Загрузите результаты, предпочтительно используя SoftReferences. Вызовите recycle() на объектах Bitmap, когда они вам больше не нужны.

Являются ли изображения перезагружаемыми каждый раз, когда вызывается .setImageUri()?

Да.

Есть ли способ сбросить эту память (или повторно использовать)?

Нет, если у вас есть Android, создайте Bitmap для вас. ImageSwitcher представляется односторонним API, в котором вы можете устанавливать изображения, но не извлекать их.

+0

Спасибо за ваш ответ. Ты прав. У меня нет конвертирования в приведенном выше, но я говорил, что не мог даже увидеть, как в этом случае будет использоваться переработанное представление (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(); 
} 
Смежные вопросы