这是骑自行车穿过图像的好方法吗?

时间:2011-05-12 19:06:16

标签: android

我想出了一个可重复使用的“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分钟(每秒循环一次图像)并且没有见到令人讨厌的“低内存”消息。

1 个答案:

答案 0 :(得分:2)

代码中的主要问题是,即使您只有少量图像,新图像也会每秒加载到堆中,因此会不断消耗内存。

执行此操作的正确方法是将图片加载一次。您只需在Map中加载所有图片并存储参考。稍后当您需要显示图片时,您不必再次加载它。

如果您有很多照片,也可以使用WeakHashMap。这样,只有当某些View使用图片时才会对图片进行引用。一旦图片不在View中,它就可用于垃圾收集。