我似乎有内存泄漏,我不知道如何修复。我已经阅读了android.com上的所有ImageSwitcher教程和示例,但这些似乎都处理了drawables文件夹中已有的drawable。我的代码允许用户用他们的相机拍摄一张或两张照片,将图像存储到SD,然后使用图像切换器来翻转"翻转"图像结束。
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错误。现在,从我通过调试的理解,当调用.setFactory(this)时,makeView()似乎被调用了两次,这看起来很好,因为ImageSwitcher的目的是在两个图像之间切换。我想知道除了SetImageUri()之外是否有更好的方法来切换图像。我没有看到任何可能泄漏的地方或可能导致问题的原因。我没有看到convertView甚至可以被利用的任何地方。有没有办法从Uri缓存图像?是否每次重新加载图像.setImageUri()被调用?有没有办法转储内存(或重用)?这是什么让我记忆犹新?
不要听起来不尊重或粗鲁,但如果没有人引用 避免内存泄漏 文章或链接到imageswitcher的javadoc,我真的更喜欢帮助? 避免内存泄漏 文章显示了几个"你不应该这样做"但从来没有表明你应该"应该"做而不是。我已经有了javadocs的链接。我正在寻找可以解释我做错的事情的人,并指出我在代码方面的更好方向(我学习最好的代码而不是模糊的抽象学术理论),而不仅仅是回流前3个链接来自谷歌搜索。 :)
感谢您的帮助! :)
编辑:10Feb2012 所以我试图加载Drawables并且IMMEDIATELY收到了一个内存不足错误,这个错误比偶尔使用.setImageUri()获得错误。以下包含mods:
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,使用需要使用另一个drawable创建SoftReference。我不知道如何使用SoftReference可能会有所帮助,因为我现在正在崩溃初始负载。
答案 0 :(得分:2)
ImageView v = (ImageView)imageSwitcher.getNextView();
BitmapDrawable bd = (BitmapDrawable) v.getDrawable();
if (bd != null)
{
Bitmap b = bd.getBitmap();
b.recycle();
}
答案 1 :(得分:0)
我想知道除了SetImageUri()之外是否有更好的方法来切换图像。
使用缓存的BitmapDrawable调用setImageDrawable()。
我没有看到任何可能泄漏的地方或可能导致问题的原因。
使用DDMS and MAT查看泄漏的位置。
我没有看到convertView甚至可以被利用的任何地方。
考虑到源代码中没有任何名为convertView
的内容,这并不奇怪。
有没有办法从Uri缓存图像?
是。使用BitmapFactory
,自行加载图片。缓存结果,最好使用SoftReferences
。
Bitmap
。
每次重新加载图像.setImageUri()被调用吗?
是
有没有办法转储内存(或重用)?
如果您让Android为您创建Bitmap
,则不会。 ImageSwitcher
似乎是单向API,您可以在其中设置图片但不能检索图片。
答案 2 :(得分: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代码)可以帮助其他人。