Java 重新加载相机预览之前的Android内存问题

Java 重新加载相机预览之前的Android内存问题,java,android,camera,memory-management,dalvik,Java,Android,Camera,Memory Management,Dalvik,在我的应用程序中,我有一个活动可以作为相机预览。我使用一个覆盖整个屏幕的SurfaceView,以及一堆叠加在上面的项目,但没有太复杂。此活动可以启动其他活动,然后这些活动将返回到相机预览 我一直非常小心地清理资源,回收位图,避免内存泄漏,等等。我可以疯狂地运行这个应用程序并测试它,但当我的手机已经开机一段时间,而其他应用程序都在内存中时,我会在恢复或创建保存相机预览的活动时安静地关机。重现崩溃的常见测试用例是反复使用应用程序、快照照片(触发处理)、启动子活动等。退出应用程序,启动一些资源/图形

在我的应用程序中,我有一个活动可以作为相机预览。我使用一个覆盖整个屏幕的SurfaceView,以及一堆叠加在上面的项目,但没有太复杂。此活动可以启动其他活动,然后这些活动将返回到相机预览

我一直非常小心地清理资源,回收位图,避免内存泄漏,等等。我可以疯狂地运行这个应用程序并测试它,但当我的手机已经开机一段时间,而其他应用程序都在内存中时,我会在恢复或创建保存相机预览的活动时安静地关机。重现崩溃的常见测试用例是反复使用应用程序、快照照片(触发处理)、启动子活动等。退出应用程序,启动一些资源/图形密集型应用程序,然后继续我的应用程序

以下是崩溃时的一些logcat输出:

03-29 14:20:02.109: ERROR/dalvikvm(6368): externalAllocPossible(): footprint 2756592 + extAlloc 15831356 + n 8640000 >= max 22409232 (space for 3821284)
03-29 14:20:02.109: ERROR/dalvikvm-heap(6368): 8640000-byte external allocation too large for this process.
03-29 14:20:02.109: ERROR/dalvikvm(6368): Out of memory: Heap Size=3835KB, Allocated=2835KB, Bitmap Size=15460KB, Limit=21884KB
03-29 14:20:02.109: ERROR/dalvikvm(6368): Trim info: Footprint=5383KB, Allowed Footprint=5383KB, Trimmed=1548KB
03-29 14:20:02.109: ERROR/GraphicsJNI(6368): VM won't let us allocate 8640000 bytes
我的活动在每一步都要进行日志记录,因此在调用super.onCreate和将上下文视图设置为我的xml布局之间的activity.onCreate中会发生这种情况。我的第一个想法是,在内存紧张的情况下,获取SurfaceHolder或SurfaceHolder方法中发生的任何事情的过程可能太多了,但这是发生之前的方式。它似乎是在setContentView中解析xml布局和构建视图对象时发生的

我的相机预览代码取自我在书籍和文章中找到的示例,所以我想知道在surfaceDestroyed中是否还有其他需要清理的地方?我是否应该尝试在该点触发垃圾收集?这种想法的原因是系统有足够的内存让应用程序在内存较少的情况下运行。这要么是因为我自己的应用程序本身没有清理干净,要么是因为系统无法足够快地为我的应用程序回收内存。我不明白的是,为什么在setContentView期间,它试图分配这么多新内存

下面是我的表面回调代码和对活动中发生的事情的解释

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.camera_preview);

    // crash occurs here

    // ...other stuff

    initControls();
}

private void initControls()
{       
    previewHolder = preview.getHolder();
    previewHolder.addCallback(surfaceCallback);
    previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    // ...other stuff
}


SurfaceHolder.Callback surfaceCallback = new Callback() {

    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(ApplicationEx.LogTag, "surfaceDestroyed");
        camera.stopPreview();
        camera.release();
        camera = null;
        isPreviewRunning = false;
    }

    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(ApplicationEx.LogTag, "surfaceCreated");
        camera = Camera.open();

        try
        {
            camera.setPreviewDisplay(previewHolder);
        }
        catch(Throwable t)
        {

        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        Log.d(ApplicationEx.LogTag, "surfaceChanged");
        if (isPreviewRunning)
        {
            Log.d(ApplicationEx.LogTag, "preview is running, stop preview");
            camera.stopPreview();
            isPreviewRunning = false;
        }
        Camera.Parameters parameters = camera.getParameters();
        setPreviewAndPictureSize(parameters, width, height);
        parameters.setPictureFormat(PixelFormat.JPEG);
        parameters.setJpegQuality(85);
        camera.setParameters(parameters);
        camera.startPreview();
        isPreviewRunning = true;
        Log.d(ApplicationEx.LogTag, "end surfaceChanged");

    }
};

停止预览并在
onPause()
中释放相机,然后在
onResume()
中获取相机。不管其他任何事情,现在,当用户按下HOME键时,您将捆绑相机并阻止其他应用程序使用它

(顺便说一句,我犯了这个错误很长一段时间,并在过去几个月的书中纠正了它)


这可能有助于改善你的记忆状况,尽管我不确定。如果你能缩小要求8640000字节分配的范围(这真的很大),这可能会有所帮助。您的版面中可能有一些背景图片?

很高兴您的回答。您将从旧版的Adv Busy Coder指南中识别SurfaceHolder.Callback。lol感谢更新的获取/发布逻辑。我没有8MB的内存。布局中有一个占位符图像,我正在上面设计覆盖图(emulator中的预览很糟糕),但大约是350k。我删除了它,看看它是否有用。@Rich:请记住,PNG/JPG文件中的350k在解压缩后会大得多。是的……预览图像是从应用程序的输出中获取的,所以在将其包含在布局中之前,我没有缩小它。1600h x 1200w x 4(argb)=几乎8MB。可能需要一些元数据来达到864万?再次感谢您的帮助。顺便说一句……您是说surfaceCreated和surfaceDestroyed中的代码应该完全移动到onResume/onPause,并且只有surfaceChanged代码应该保留在SurfaceHolder.Callback中?@Rich:如果您查看更新的示例,最好是: