调用"DisplayManagerGlobal.getDisplayInfo()"导致应用程序无响应(ANR)

4

显然,应用程序中的某些内容从不同的线程(主线程和Binder线程)调用了该方法,导致了内部ANR。这种情况经常发生,我不知道它发生在哪里,因为我无法在模拟器或测试设备上复现它。

应用程序功能:这是一个应用锁屏应用程序,在应用程序覆盖层上绘制全屏锁定视图,并要求输入密码(图案),还支持指纹解锁机制。为了成功监听指纹,我们还必须使用透明活动使应用程序进入“前台”,然后等待指纹输入,否则指纹机制将无法监听,并且也没有回调函数。也许这就是问题所在,因为活动频繁启动和完成,每次锁定应用都会如此。

ANR报告涉及以下两个问题:

Broadcast of Intent { act=android.intent.action.SCREEN_ON flg=0x50200010 (has extras) } Broadcast of Intent { act=android.intent.action.SCREEN_OFF flg=0x50200010 (has extras) }

关于这个问题,应用程序有一个始终运行的前台服务,因为它需要随时锁定应用程序作为要求,并从动态广播接收器中监听这些事件。但是,在它们的任何一个中都没有阻塞调用或同步调用,只有一个连续的工作线程的启动停止机制,该线程检查当前的前台应用程序。

完整的ANR报告

相关的ANR报告(相关部分,从Android 10设备,API 29报告):

"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x71773f98 self=0x7f95a7d000
  | sysTid=28841 nice=-4 cgrp=default sched=0/0 handle=0x7f96febee8
  | state=S schedstat=( 151471598466 24930775143 453988 ) utm=9534 stm=5612 core=3 HZ=100
  | stack=0x7fddb4d000-0x7fddb4f000 stackSize=8192KB
  | held mutexes=
  at android.hardware.display.DisplayManagerGlobal.getDisplayInfo (DisplayManagerGlobal.java:177)
- waiting to lock <0x038dbdd7> (a java.lang.Object) held by thread 6
  at android.view.Display.updateDisplayInfoLocked (Display.java:1214)
  at android.view.Display.updateDisplayInfoLocked (Display.java:1209)
  at android.view.Display.getState (Display.java:1174)
- locked <0x053b6aeb> (a android.view.Display)
  at android.view.ViewRootImpl$1.onDisplayChanged (ViewRootImpl.java:1601)
  at android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate.handleMessage (DisplayManagerGlobal.java:1417)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:237)
  at android.app.ActivityThread.main (ActivityThread.java:7811)
  at java.lang.reflect.Method.invoke (Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1068)

"Binder:28841_6" prio=5 tid=6 Runnable
  | group="main" sCount=0 dsCount=0 flags=0 obj=0x132c19b0 self=0x7f08d9a400
  | sysTid=29512 nice=0 cgrp=default sched=0/0 handle=0x7f0133ed50
  | state=R schedstat=( 673333745225 29381013522 76199 ) utm=67111 stm=221 core=7 HZ=100
  | stack=0x7f01248000-0x7f0124a000 stackSize=991KB
  | held mutexes= "mutator lock"(shared held)
  at android.os.MessageQueue.enqueueMessage (MessageQueue.java:581)
- locked <0x0306e956> (a android.os.MessageQueue)
  at android.os.Handler.enqueueMessage (Handler.java:754)
  at android.os.Handler.sendMessageAtTime (Handler.java:703)
  at android.os.Handler.sendMessageDelayed (Handler.java:673)
  at android.os.Handler.sendMessage (Handler.java:611)
  at android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate.sendDisplayEvent (DisplayManagerGlobal.java:1403)
  at android.hardware.display.DisplayManagerGlobal.handleDisplayEvent (DisplayManagerGlobal.java:408)
- locked <0x038dbdd7> (a java.lang.Object)
  at android.hardware.display.DisplayManagerGlobal.access$100 (DisplayManagerGlobal.java:70)
  at android.hardware.display.DisplayManagerGlobal$DisplayManagerCallback.onDisplayEvent (DisplayManagerGlobal.java:1354)
  at android.hardware.display.IDisplayManagerCallback$Stub.onTransact (IDisplayManagerCallback.java:119)
  at android.os.Binder.execTransactInternal (Binder.java:1021)
  at android.os.Binder.execTransact (Binder.java:994)

我不确定具体是什么导致了这个问题,因为在任何线程中都没有堆栈跟踪指向应用本身。我可以看到主线程由于 等待锁定 <0x038dbdd7> 而被阻塞,因为 binder 线程已经锁定了它: 锁定了 <0x038dbdd7> 但没有释放。

对此有什么看法吗?非常感谢您提供的任何帮助。


似乎是Android本身的奇怪竞争条件。您是否有ANR报告提供的其他信息?我不熟悉这些报告,但logcat或更多细节可能会有所帮助。 - JensV
很遗憾,不行。堆栈跟踪是从开发者控制台提供的,从ANR和崩溃选项卡中获取。我也无法在本地重现ANR。 - Furkan Yurdakul
@JensV 实际上,如果有帮助的话,原因似乎是“Broadcast of Intent { act = android.intent.action.SCREEN_OFF flg = 0x50200010(具有附加信息)}”,我现在正在将其作为编辑添加到问题的堆栈跟踪中。 - Furkan Yurdakul
@JensV 在问题中添加了一些额外的信息。 - Furkan Yurdakul
肯定有些奇怪...因为线程6的状态是“Runnable”,理论上应该被分配一些时间来完成发送消息。我猜你没有其他正在运行的线程的信息。一个猜测是系统非常忙,线程6根本没有时间完成,但这似乎有点不太可能。 - JensV
显示剩余6条评论
1个回答

2

好的,我找到了问题所在。这是由于Android的WindowManagerGlobal类下的内部内存泄漏引起的。以下是它引起的原因:

该应用程序正在使用悬浮窗口权限,并且我们要求用户授权,如果用户授权,则自动检测。此后,我们还创建了一个前台服务,在2秒的间隔循环中运行,永不停止,同时也在检查权限。由于我遇到了一些关于Settings.canDrawOverlays(Context)方法的错误,我决定寻找答案,并找到了这个答案,它导致了巨大的内存泄漏。

你看,调用绘制一个不可见的覆盖层部分会创建一个泄漏。它将视图根添加到内存中,但在此期间,由于SecurityException而无法添加覆盖层,视图永远不会被删除。由于该代码每次检查权限都会创建一个新视图,新视图根、新显示和新监听器都会调用onDisplayChanged。经过调试,我最终能够看到这一点,并捕获了内存泄漏。删除该代码完全解决了问题,现在没有与之相关的ANR可见。

我还尝试删除调用WindowManager.removeView(view)的视图,如果addView失败,但无论如何,未能添加的视图仍然留在监听器和内存中。因此,我认为应该避免使用这种方法,并找到其他替代方案。


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