#android
#Android
Вопрос:
Я придумал класс «Bitmap Helper» для многократного использования, чтобы помочь моему приложению циклически просматривать полноэкранные изображения. Я сделал это, потому что потоку пользовательского интерфейса необходимо обновить ImageView, и я не хочу использовать глобальные переменные в своей деятельности для передачи ссылок 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()
}
для тестирования я установил константы.MAX_AD_TIME_SECONDS = 1 и приложение циклически просматривает изображение каждую секунду. Наблюдать за этим здорово, но ActivityManager отключает другие процессы.
я делаю это неправильно или правильно?
размер изображений составляет от 130 до 280 КБ
РЕДАКТИРОВАТЬ: как и предлагалось, я обновил свой класс 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;
}
}
Вместо использования loadImageAsset(), я теперь использую showImageInBuffer() для отображения изображения в пользовательском интерфейсе.
Я тестировал эту реализацию в течение 15 минут (циклически просматривая изображения каждую секунду), и никаких неприятных сообщений о нехватке памяти замечено не было.
Комментарии:
1.старайтесь не загружать каждое изображение, используя
BitmapFactory.decodeStream(istr);
каждый раз. сохраните ссылку на них в чем-то вродеSoftReference
Ответ №1:
Основная проблема вашего кода заключается в том, что, хотя у вас всего несколько изображений, новое изображение загружается в кучу каждую секунду, и поэтому память постоянно расходуется.
Правильный способ сделать это — загрузить изображения один раз. Вы можете просто загрузить все изображения и сохранить ссылки на них в Map
. Позже, когда вам понадобится показать картинку, вам не придется загружать ее снова.
Вы также можете использовать WeakHashMap
, если у вас много изображений. Таким образом, ссылки на изображения будут там только тогда, когда они используются некоторыми View
. Как только изображения не окажется в View
, оно будет доступно для сборки мусора.
Комментарии:
1. Я использовал Map в своем обновленном решении. Судя по следующему образцу кода, Map и WeakHashMap легко взаимозаменяемы… devdaily.com/blog/post/java /…
2. Да,
Map
это универсальный интерфейс, который имеет разные реализации. Таким образом, вы можете использовать любое из них, соответствующее вашим потребностям. Я предложил WeakHashMap, потому что он позволяет сборщику мусора восстанавливать изображения, когда они больше не используются, но вы, конечно, можете использовать другие реализации. Когда вы работаете с интерфейсом, а не с реальными реализациями, их очень легко переключать.3. Я раньше не использовал WeakHashMap — чтобы дифференцировать их использование, подходят ли следующие варианты использования? 1) используйте Map, когда изображения должны быть доступны в течение всего срока действия действия 2) используйте WeakHashMap, когда изображения не обязательно должны быть доступны 100% времени, например, когда пользователь случайно просматривает список изображений или галерею загруженных изображений.
4. Ну, технически, когда вы используете
WeakHashMap
, это все еще конкретная реализация Map. Итак, я предполагаю, что в случае 1) вы имеете в виду «обычную» реализацию, подобнуюHashMap
. Да, ваши случаи верны, при использованииWeakHashMap
вы разрешаете GC сохранять некоторую кучу, когда объект не используется, но вам придется создать этот объект снова, если он вам понадобится позже, и GC его собрал.5. В случае 1) Я действительно имел в виду HashMap. Спасибо, что нашли время помочь мне: приветствую: