调用 "DisplayManagerGlobal.getDisplayInfo()" 导致应用程序无响应 (ANR)
Call to "DisplayManagerGlobal.getDisplayInfo()" causes App Not Responding (ANR) in the app
显然,应用程序中的某些内容从不同线程(主线程和活页夹线程)调用方法,这会导致内部 ANR。它经常发生,我不知道它发生在哪里,因为我无法在模拟器或我拥有的测试设备上重现它。
应用程序的作用: 这是一个应用程序储物柜应用程序,它在应用程序覆盖上绘制全屏锁定视图并要求输入密码(图案),还支持指纹解锁机制。要成功监听指纹,我们还得用一个透明的activity让app走"foreground",然后等待指纹输入,否则指纹机制不监听也不会也有一个回调。也许这就是原因,因为 activity 在每个锁定的应用程序上频繁启动和结束。
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 报告(相关部分,来自 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.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.access0 (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)
我不确定是什么原因导致的,因为在任何线程中都没有到应用程序本身的堆栈跟踪。我可以看到主线程由于 waiting to lock <0x038dbdd7>
而被阻塞,因为活页夹线程已经锁定了它:locked <0x038dbdd7>
但没有释放它。
对这件事有什么看法吗?感谢您的帮助,非常感谢。
嗯,我找到问题了。这是由于 Android 的 WindowManagerGlobal
class 下的内部内存泄漏引起的。原因如下:
该应用正在使用覆盖权限,这样做我们要求用户授予权限,如果用户授予权限,则会自动检测到。在此之后,我们还一直在创建一个前台服务,它在循环中以 2 秒的间隔运行,永不停止,还检查其间的权限。由于我遇到了一些关于 Settings.canDrawOverlays(Context)
方法的错误,我决定查找答案,并发现 导致了巨大的内存泄漏。
你看,将绘图称为不可见的覆盖部分会造成泄漏。它将视图根添加到内存中,但在两者之间,由于 SecurityException
而无法添加覆盖,并且视图永远不会被删除。由于该代码每次都会创建一个新视图来检查权限、新视图根、新显示和新的调用侦听器 onDisplayChanged
。调试后,我终于能够看到这一点并抓住了内存泄漏。删除该代码完全解决了问题,现在看不到与之相关的 ANR。
如果 addView
失败,我也尝试删除调用 WindowManager.removeView(view)
的视图,但不管要添加的失败视图是否保留在侦听器和内存中。所以,我想说无论如何都应该避免这种方法,应该找到另一种替代方法。
显然,应用程序中的某些内容从不同线程(主线程和活页夹线程)调用方法,这会导致内部 ANR。它经常发生,我不知道它发生在哪里,因为我无法在模拟器或我拥有的测试设备上重现它。
应用程序的作用: 这是一个应用程序储物柜应用程序,它在应用程序覆盖上绘制全屏锁定视图并要求输入密码(图案),还支持指纹解锁机制。要成功监听指纹,我们还得用一个透明的activity让app走"foreground",然后等待指纹输入,否则指纹机制不监听也不会也有一个回调。也许这就是原因,因为 activity 在每个锁定的应用程序上频繁启动和结束。
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 报告(相关部分,来自 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.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.access0 (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)
我不确定是什么原因导致的,因为在任何线程中都没有到应用程序本身的堆栈跟踪。我可以看到主线程由于 waiting to lock <0x038dbdd7>
而被阻塞,因为活页夹线程已经锁定了它:locked <0x038dbdd7>
但没有释放它。
对这件事有什么看法吗?感谢您的帮助,非常感谢。
嗯,我找到问题了。这是由于 Android 的 WindowManagerGlobal
class 下的内部内存泄漏引起的。原因如下:
该应用正在使用覆盖权限,这样做我们要求用户授予权限,如果用户授予权限,则会自动检测到。在此之后,我们还一直在创建一个前台服务,它在循环中以 2 秒的间隔运行,永不停止,还检查其间的权限。由于我遇到了一些关于 Settings.canDrawOverlays(Context)
方法的错误,我决定查找答案,并发现
你看,将绘图称为不可见的覆盖部分会造成泄漏。它将视图根添加到内存中,但在两者之间,由于 SecurityException
而无法添加覆盖,并且视图永远不会被删除。由于该代码每次都会创建一个新视图来检查权限、新视图根、新显示和新的调用侦听器 onDisplayChanged
。调试后,我终于能够看到这一点并抓住了内存泄漏。删除该代码完全解决了问题,现在看不到与之相关的 ANR。
如果 addView
失败,我也尝试删除调用 WindowManager.removeView(view)
的视图,但不管要添加的失败视图是否保留在侦听器和内存中。所以,我想说无论如何都应该避免这种方法,应该找到另一种替代方法。