在我的应用程序中,我有一个充当相机预览的 Activity。我使用了一个横跨整个屏幕的 SurfaceView,以及一堆覆盖在它上面的项目,但没什么太复杂的。此活动可以启动其他活动,然后这些活动将返回到相机预览。
我一直非常小心地清理资源,回收位图,避免内存泄漏等。我可以运行这个应用程序并疯狂地测试它,但是当我的手机已经打开一段时间并且其他应用程序在内存中时,我得到恢复或创建包含相机预览的 Activity 时静默关闭。重现崩溃的常见测试用例是一遍又一遍地使用应用程序、拍摄照片(触发处理)、启动子活动等。退出应用程序,启动一些资源/图形繁重的东西,然后恢复我的应用程序。
以下是崩溃时的一些 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 方法中发生的任何事情的过程可能太多了,但这是在此之前发生的事情。在解析我的 xml 布局和构建 View 对象时,它似乎发生在 setContentView 中。
我的相机预览代码取自我在书籍和文章中找到的示例,所以我想知道在 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");
}
};