重新加载相机预览之前出现 Android 内存问题
在我的应用程序中,我有一个充当相机预览的活动。我使用覆盖整个屏幕的 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");
}
};
In my app, I have an Activity that acts as a camera preview. I use a SurfaceView stretched across the entire screen, along with a bunch of items overlaid on top of it but nothing too complicated. This Activity can launch other Activities which will then return back to the camera preview.
I've been very careful to clean up resources, recycle bitmaps, avoid memory leaks, etc. I can run this app and test it like crazy, but when my phone has been on for a while and other apps are in memory, I get silent shut downs when resuming or creating the Activity that holds the camera preview. A common test case to reproduce the crash is use the app, snap photos (which triggers processing), launch sub activities, etc over and over again. Back out of the app, fire up something resource/graphics heavy, and then resume my app.
Here is some logcat output at the time of the crash:
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
My activity is logging at every step, so this happens in Activity.onCreate in between calling super.onCreate and setting the context view to my xml layout. My first thought was that the process of acquiring the SurfaceHolder or whatever happens in the SurfaceHolder methods might be too much in a tight memory situation, but this is way before that happens. It seems that it's occurring in setContentView while parsing my xml layout and building the View objects.
My camera preview code is taken from examples I've found in books and articles, so I'm wondering if there is any additional cleanup I need to do in surfaceDestroyed? Should I try and trigger a garbage collection at that point? The reason for this thinking is that the system has enough memory for the app to run in conditions where less apps are in memory. It either has to do with my own app not cleaning up after itself enough, or the system not being able to reclaim memory fast enough for my app. What I don't understand is why during setContentView is it attempting to allocate so much new memory.
Here is my surface callback code and a paraphrase of what goes on in the activity
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");
}
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在
onPause()
中停止预览并释放相机,并在onResume()
中获取它。不管怎样,现在,当用户按 HOME 时,您将绑定相机并阻止其他应用程序使用它。(顺便说一句,我很长时间以来都犯了这个错误,并在过去的几个月里在我的书中纠正了它)
这可能会对您的记忆状况有所帮助,尽管我不确定。如果您可以缩小到底要求 8640000 字节分配的范围(这确实很大),那可能会有所帮助。也许您的布局中有一些背景图片?
Stop the preview and release the camera in
onPause()
, and acquire it inonResume()
. Regardless of anything else, right now, you are going to tie up the camera and prevent other applications from using it when the user presses HOME.(BTW, I had this wrong for a long time and corrected it in my books over the past few months)
It is possible that this will help with your memory situation, though I am not certain. If you could narrow down what exactly is asking for a 8640000-byte allocation (which is really really big), that might help. Perhaps some background image in your layout?