位图回收和内存管理

时间:2013-06-01 14:56:46

标签: android performance android-imageview android-canvas

在Activity-A中,我使用Surfaceview和Bitmap作为背景。当我转到onPause时,我释放它并将其设置为null并创建一个显式GC。它工作正常。但是当我回到相同的Activity时 - 分配了近3MB的巨大堆来解码位图。这是因为我在GC之后解码位图。

我很好地回收了位图和GC流程。但我担心堆分配会增加,以便处理相同的位图。

当我移动下一个Activity时,我需要将它保存在某个不应该占用堆空间的地方,当我回来时我应该选择图像。知道如何实现这个目标吗?

以下是现有代码

    @Override
    protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();
    ourSurfaceView.pause(); 
    Log.i("DragDrop", "In pause drag drop");  
        backGround.release(); 
        optionSelected.release(); 

        backgoundImage.recycle();  

        backgoundImage=null;
        backGround=optionSelected=null;

    if (tts != null) { 
        Log.i("DragDrop", "In pause drag drop stop");
        tts.stop();
        tts.shutdown();
    }  
    System.gc();

} 



    public void run() {
        // TODO Auto-generated method stub
//      Log.i("Notice", "In run of mybringback"); 
        if(backgoundImage == null){ 
            try {
                Log.i("MyBringBack", "In run of mybringback"); 
                backgoundImage = Bitmap.createScaledBitmap(getAssetImage(getApplicationContext(),"backgroundhomepage"), (int) dWidth, (int) dHeight, true);

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        ourHolder = getHolder();
        while (isRunning) {
//          Log.i("DragDrop", "ourHolder.getSurface().isValid()" +  ourHolder.getSurface().isValid() );
            if (!ourHolder.getSurface().isValid()){
                continue;
            } 
            canvas = ourHolder.lockCanvas();    
            screenCenterX = dWidth / 2;
            screenCenterY = dHeight / 2; 
            canvas.drawBitmap(backgoundImage, 0, 0, null);   
            if (imagePublishDone) {
                if(!welcomeDone){ 
                    message = "Drop your wish to panda";
                    tts.speak(message, TextToSpeech.QUEUE_FLUSH, null);
                    welcomeDone=true;
                }
                moveImageInEllipticalPath();
            } else {
                initialImagePublish();
            }

            centreReached = false;
            ourHolder.unlockCanvasAndPost(canvas);
        }
    } 


    public  Bitmap getAssetImage(Context context, String filename) throws IOException {
        AssetManager assets = getApplicationContext().getResources().getAssets();
        InputStream buffer = null;
        try {
            buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inTempStorage = new byte[16*1024];   
        options.inPurgeable = true; 
        Bitmap temp = BitmapFactory.decodeStream(buffer, null, options);
        Bitmap finalImage = Bitmap.createScaledBitmap(temp, (int) dWidth, (int) dHeight, true);
        temp.recycle();
        temp=null; 
        return finalImage;
    }

1 个答案:

答案 0 :(得分:0)

我认为解决问题的更好方法是在解码Bitmap时使用inPurgeable标志。

这样当系统需要回收内存时,它会在内部“回收”位图,但是当需要再次显示位图时,它会自动加载它。

[编辑]

您的代码存在一些问题。

  1. inTempStorage中的getAssetImage(...)无用。这种“优化”应该为解码器提供临时计算所需的存储器。如果您不提供它,解码器将自行分配它。如果您通过多个解码分摊该分配的成本,这只是一种优化。但是,您的解决方案每次都会进行分配。不妨让解码器这样做,因为它确切地知道它需要多少内存。

  2. 看起来你在缩放你的位图时浪费了很多内存。 getAssetImage(...)解码一个完整大小的图像,然后再缩放,返回和缩放。这是很多额外的工作和记忆。您应该考虑通过inSampleSize对图像进行采样。要找出正确的采样因子,您可以使用inJustDecodeBounds

  3. 最后,您对inPurgeable的使用不正确。系统只能清除使用该选项解码的位图。但是,如上所述,您可以在加载可清除的位图后继续创建新的位图。此外,即使您修改了设计以便只加载正确的位图,我也不认为系统能够为您重新加载它,因为您使用的是decodeStream(...)。流不能以通用方式重新打开和重绕,因此系统将无法重新读取该数据。尝试从文件解码。您还可以使用inInputShareable来更好地控制清除行为。