Android - 视图.Surface OutOfResourcesException

4
我的 Android 应用似乎在使用 ListView 导航和标准菜单键时没有释放其视图。经过大约一百次左右(10个或更少的视图)加载后,它开始变得卡顿并出现黑屏。
错误日志:
07-01 09:54:42.913: INFO/ActivityManager(1279): Starting: Intent { cmp=com.site.android.conferencecompanion/.Search } from pid 31290
07-01 09:54:43.013: ERROR/msm7x30.gralloc(1279): /dev/pmem: no more pmem available
07-01 09:54:43.013: ERROR/msm7x30.gralloc(1279): couldn't open pmem (No such file or directory)
07-01 09:54:43.013: ERROR/msm7x30.gralloc(1279): gralloc failed err=Out of memory
07-01 09:54:43.013: WARN/GraphicBufferAllocator(1279): alloc(480, 800, 1, 00000133, ...) failed -12 (Out of memory)
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279): Allocated buffers:
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x290740: 1500.00 KiB |  480 ( 480) x  800 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x307448:   60.00 KiB |  102 ( 128) x  120 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x32e4c0:   71.25 KiB |  480 ( 480) x   38 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x3caad8:   60.00 KiB |  102 ( 128) x  120 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x4a47f8: 1346.25 KiB |  480 ( 480) x  718 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x4f9710: 1500.00 KiB |  480 ( 480) x  800 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x54c500: 1500.00 KiB |  480 ( 480) x  800 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x5d1c00: 1500.00 KiB |  480 ( 480) x  800 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x5f5f98: 1500.00 KiB |  480 ( 480) x  800 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x604600:   60.00 KiB |  126 ( 128) x  120 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x60a3d0:  750.00 KiB |  480 ( 480) x  800 |        4 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x661270: 1428.75 KiB |  480 ( 480) x  762 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x6830b8:  750.00 KiB |  480 ( 480) x  800 |        4 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x70e0e8: 1500.00 KiB |  480 ( 480) x  800 |        1 | 0x00000133
07-01 09:54:43.013: DEBUG/GraphicBufferAllocator(1279):   0x71f238:   71.25 KiB |  480 ( 480) 
07-01 09:54:43.013: ERROR/SurfaceFlinger(1279): Layer::requestBuffer(this=0x189d50), index=0, w=480, h=800 failed (Out of memory)
07-01 09:54:43.013: ERROR/Surface(31290): Surface (identity=4545) requestBuffer(0, 0, 0, 0, 00000033) returned a buffer with a null handle
07-01 09:54:43.013: ERROR/Surface(31290): getBufferLocked(0, 0, 0, 0, 00000033) failed (Out of memory)
07-01 09:54:43.013: ERROR/Surface(31290): dequeueBuffer failed (Out of memory)
07-01 09:54:43.013: ERROR/ViewRoot(31290): OutOfResourcesException locking surface
07-01 09:54:43.013: ERROR/ViewRoot(31290): android.view.Surface$OutOfResourcesException
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.view.Surface.lockCanvasNative(Native Method)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.view.Surface.lockCanvas(Surface.java:314)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.view.ViewRoot.draw(ViewRoot.java:1457)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.view.ViewRoot.performTraversals(ViewRoot.java:1259)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1860)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.os.Handler.dispatchMessage(Handler.java:99)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.os.Looper.loop(Looper.java:123)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at android.app.ActivityThread.main(ActivityThread.java:3839)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at java.lang.reflect.Method.invokeNative(Native Method)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at java.lang.reflect.Method.invoke(Method.java:507)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
07-01 09:54:43.013: ERROR/ViewRoot(31290):     at dalvik.system.NativeStart.main(Native Method)
07-01 09:54:43.203: INFO/ActivityManager(1279): Displayed com.site.android.conferencecompanion/.Search: +292ms

adb shell dumpsys window 显示如下内容:

#1 - #29, junk
#30: AppWindowToken{40bbc000 token=HistoryRecord{408cc260 com.site.android.conferencecompanion/.ProgramDates}}

...

#142: AppWindowToken{40ba65a8 token=HistoryRecord{40b93808 com.site.android.conferencecompanion/.ProgramSpeakers}}

如果我理解正确,大约有112个视图被保存在内存中。我能做些什么来解决这个问题吗?是否有检查、标志或参数我漏掉了?我是否误解了转储文件?

谢谢!


你是否有任何对Context或View的静态引用(隐含持有一个context)? - Scott Ferguson
@Scott:我不这么认为,我怎么能确定呢? - Josh
简单使用静态关键字是一个不错的起点。这里有一些关于Android内存泄漏的基础文档:http://developer.android.com/resources/articles/avoiding-memory-leaks.html - Scott Ferguson
请问您能否澄清一下您在代码中正在做什么?这是在一个活动内发生的吗?还是当用户选择ListView项目时,通过调用意图来启动多个活动?提供一段关于如何启动/加载新视图的代码示例可能会有所帮助。 - CatalystNZ
7个回答

3
检查泄漏来源的一个好方法是,如果您在Eclipse中,请选择Window->Open Perspective->DDMS,然后从进程选择中选择正在运行的进程并使用分配跟踪器。启动应用程序,但在开始分配跟踪器之前不要触摸应用程序。然后执行您认为会导致问题的操作,每次检查分配情况。这应该能够显示出导致泄漏的确切代码。
此外,如果您发布您的代码,我们可以查看它。

我会尝试一下,不幸的是整个应用程序似乎都在这样做,所以我没有一个可以发布的单个代码实例。也许我只能获取一个单独的活动。 - Josh

1

虽然这是一年前的事情,但我是通过谷歌来到这里的。

有些东西告诉我你同时加载了太多的视图,而 Android 无法跟踪它们。 * 这听起来像是没有正确使用 ListView,这是我们中很多人都会犯的错误。假设你有一个自定义的数组适配器:

public abstract View getView (int position, View convertView, ViewGroup parent)

当您超载此函数时,请始终检查convertView。

public abstract View getView (int position, View convertView, ViewGroup parent)
{
   View view = convertView;
   if ( view == null )
   {
      // create/inflate the view here
      // ex: view = inflater.inflate(R.layout.bob, null);
      // configure the static parts here
   }
   // configure the dynamic parts here

   return view;
}

Android在listview视图中有一个“回收站”机制,如果可能的话会重复使用视图。换句话说,它不会创建一堆不同的视图,而是从回收站中获取旧视图。

在您的情况下,您有10种不同的视图。如果它们非常相似,您可以制作一个父视图,并打开或关闭视图的部分(例如subView.setVisibility(View.GONE))。只要注意大量的if语句块即可。我以前没有使用过这些,但您应该进行调查。

public abstract int getItemViewType (int position)
public abstract int getViewTypeCount ()

这样做会更好,因为您不必处理额外的废话,而且它应该由Android自动管理

  • 就我个人而言,我并不100%确定这是否是正确的,但我有一种66%的感觉是这样的

0

我曾经遇到同样的问题,最终解决了。

解决方案:

从Eclipse启动您的应用程序,并在不断观察Eclipse中的LogCat控制台的同时进行一些操作。当启动新活动或类似操作时,您应该会看到:

Tag:"WindowManagerImpl", Text:"addView, new view, mViews[1]:....".

这里重要的是 mViews[x]-part。x 告诉你有多少视图处于活动状态。 当您启动一个活动但 mViews 计数器告诉您有 5-10 个新视图时,那么您可以确定您意外地创建了太多视图。通过这种方式,您可以找到代码中的弱点。
在我的游戏应用程序中发生了这种情况:每当我输掉游戏时,都会启动 5 个或更多的 GameOverActivity,因为启动新的 GameOverActivity 的代码位于循环中。 因此,在玩几分钟后,就有 20 个未使用的 GameOverViews 消耗我的资源。(因此,在循环中启动活动时请谨慎。)

0

尝试扩展您的活动以查看它们是否被垃圾回收(即调用finalize())。同样,尝试扩展您的视图以帮助识别它们是否被垃圾回收。

如果您正在将视图和活动放入缓存集合中,请尝试使用使用WeakReference的集合对象,例如WeakHashMap。您在视图或活动中使用内部类吗?如果是这样,这些类会持有对活动或视图的引用,并防止它们被垃圾回收。一个很好的例子是作为内部类的AsyncTask。该任务在自己的线程中运行,并且甚至在关闭活动后仍然可以继续持有视图。

此外,查看静态变量,它们是否持有对象?


0
这是与Surface Flinger相关的问题,它将应用程序的Surfaces维护和组合到显示设备中。
尝试这个:在xml文件中减少您的布局像素格式。

0

如果我可以在这里留个便条:

当我在我的Prestigo PMP5080B上进行测试时,我也遇到了同样的日志问题。 我的应用程序也会随机阻塞。

我注意到,当我断开USB电缆(通过在设置中单击关闭连接即可)时,应用程序可以正常工作。 我还注意到,在使用Eclipse和USB连接我的应用程序时,SD卡无法挂载,但不知道这个问题是否与我的应用程序有关,因为我没有存储在SD卡上。

希望对你有所帮助


0
在我的情况下,错误出现是因为我使用Window类来更改状态栏颜色,而这仅支持Android构建版本21及以上。将以下代码添加到您的代码中,它可能会起作用。
if(Build.VERSION.SDK_INT >= 21) {

//... your code with Window class here

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接