我想出了一个可重复使用的“BitmapHelper”类来帮助我的应用程序循环显示全屏图像。 我这样做是因为UI线程需要更新ImageView,我不想在我的Activity中使用全局变量来传递Bitmap和ImageView引用。
以下代码有效但我观察内存不足:没有更多后台进程让我担心内存泄漏
BitmapHelper类:
public class BitmapHelper
{
//member variables
private ImageView mImageView;
private Bitmap mBitmap;
private Activity mActivity;
//constructor requires Activity and ImageView to work on
// requiring the ImageView here implies that one BitmapHelper is used per ImageView
public BitmapHelper(Activity activity, ImageView imageview)
{
mActivity = activity;
mImageView = imageview;
}
//loadImageAsset(name of asset, i.e. "image.png")
public void loadImageAsset(String assetfilename)
{
boolean bError = false;
try
{
mBitmap = getBitmapFromAsset(assetfilename);
}
catch (IOException e)
{
e.printStackTrace();
bError = true;
}
//assign Bitmap to Imageview
//NOTE!! this must be done in the UI thread or it's
// crash time when executed from another thread
if (bError == false)
{
mActivity.runOnUiThread(new Runnable()
{
public void run()
{
mImageView.setImageBitmap(mBitmap);
}
});
}
}
//getBitmapFromAsset(name of asset, i.e. "image.png")
public Bitmap getBitmapFromAsset(String assetfilename) throws IOException
{
AssetManager assetManager = mActivity.getAssets();
InputStream istr = assetManager.open(assetfilename);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
return bitmap;
}
}
活动:
public class myActivity extends Activity
{
//member variables
private String mFilenames[];
private int mFilenameIndex;
private ImageView mImageView;
private Timer mTimerSeconds;
private int mIntIdleSeconds;
private BitmapHelper mBitmapHelper;
//for logging
private final String TAG = this.getClass().getSimpleName();
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.standby);
//get references to UI components
mImageView = (ImageView) findViewById(R.id.standby_imageview_adtop);
//image slideshow
mFilenames = new String[]{"pic1.png","pic2.png", "pic3.png"};
mFilenameIndex = 0;
mBitmapHelper = new BitmapHelper(this, mImageView);
mBitmapHelper.loadImageAsset(mFilenames[mFilenameIndex]);
//timer
startSlideshowTimer();
}
@Override
protected void onDestroy()
{
Log.d(TAG, "onDestroy()");
if (mTimerSeconds != null)
{
mTimerSeconds.cancel();
}
super.onDestroy();
}
/**
* timer functions
*/
//start the slideshow timer
public void startSlideshowTimer()
{
//initialize idle counter
mIntIdleSeconds=0;
//create timer to tick every second
mTimerSeconds = new Timer();
mTimerSeconds.schedule(new TimerTask()
{
@Override
public void run()
{
timerSecondsCounter();
}
}, 0, 1000);
}
//called every second to count idle time and to update clock on Welcome screen
private void timerSecondsCounter()
{
mIntIdleSeconds++;
if (mIntIdleSeconds == Constants.MAX_AD_TIME_SECONDS)
{
//cycle to next image
mFilenameIndex++;
if (mFilenameIndex == mFilenames.length)
{
mFilenameIndex=0;
}
mBitmapHelper.loadImageAsset(mFilenames[mFilenameIndex]);
//reset counter
mIntIdleSeconds=0;
}
}//end of: timerSecondsCounter()
}
进行测试,我设置 Constants.MAX_AD_TIME_SECONDS = 1 ,应用程序每秒循环一次图像。看起来很酷但是ActivityManager正在关闭其他进程。
我做错了还是对的?
注意:图像大约130KB到280KB
编辑:按照建议我更新了我的BitmapHelper类来预加载图像。请参阅以下内容......
public class BitmapHelper
{
//member variables
private ImageView mImageView;
private Bitmap mBitmap;
private Activity mActivity;
private Map<String,Bitmap> mBitHashmap;
//constructor requires Activity and ImageView to work on
// requiring the ImageView here implies that one BitmapHelper is used per ImageView
public BitmapHelper(Activity activity, ImageView imageview)
{
mActivity = activity;
mImageView = imageview;
mBitHashmap = new HashMap<String,Bitmap>();
}
//loadAndShowImageAsset(name of asset, i.e. "image.png")
//NOTE: avoid doing too many of these, otherwise the heap is used-up
// and the ActivityManager starts closing down background activities
public void loadAndShowImageAsset(String assetfilename)
{
boolean bError = false;
try
{
mBitmap = getBitmapFromAsset(assetfilename);
}
catch (IOException e)
{
e.printStackTrace();
bError = true;
}
//assign Bitmap to Imageview
//NOTE!! this must be done in the UI thread or it's
// crash time when executed from another thread
if (bError == false)
{
mActivity.runOnUiThread(new Runnable()
{
public void run()
{
mImageView.setImageBitmap(mBitmap);
}
});
}
}
//putImageInBuffer(name of asset, i.e. "image.png")
public void putImageInBuffer(String assetfilename)
{
try
{
Bitmap bitmap = getBitmapFromAsset(assetfilename);
if (bitmap != null)
{
mBitHashmap.put(assetfilename, bitmap);
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
//showImageInBuffer(name of asset, i.e. "image.png")
//NOTE: pre-load all the images to memory and then use this to set the
// image in the ImageView. This is the heap-friendly way to do it.
public void showImageInBuffer(String assetfilename)
{
mBitmap = mBitHashmap.get(assetfilename);
if (mBitmap != null)
{
//assign Bitmap to Imageview
//NOTE!! this must be done in the UI thread or it's
// crash time when executed from another thread
mActivity.runOnUiThread(new Runnable()
{
public void run()
{
mImageView.setImageBitmap(mBitmap);
}
});
}
}
//getBitmapFromAsset(name of asset, i.e. "image.png")
public Bitmap getBitmapFromAsset(String assetfilename) throws IOException
{
AssetManager assetManager = mActivity.getAssets();
InputStream istr = assetManager.open(assetfilename);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
return bitmap;
}
}
我现在使用showImageInBuffer()来显示UI上的图像,而不是使用“loadImageAsset()”。 我一直在测试这个实现15分钟(每秒循环一次图像)并且没有见到令人讨厌的“低内存”消息。
答案 0 :(得分:2)
代码中的主要问题是,即使您只有少量图像,新图像也会每秒加载到堆中,因此会不断消耗内存。
执行此操作的正确方法是将图片加载一次。您只需在Map
中加载所有图片并存储参考。稍后当您需要显示图片时,您不必再次加载它。
如果您有很多照片,也可以使用WeakHashMap
。这样,只有当某些View
使用图片时才会对图片进行引用。一旦图片不在View
中,它就可用于垃圾收集。