Это хорошее решение для циклического перебора изображений?

#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. Спасибо, что нашли время помочь мне: приветствую: