Android 关于设备定屏/黑屏/冻屏/ANR那些事
定屏/黑屏常见问题
我的理解是冻屏和定屏是一个意思.
冻屏:目的就是防止执行默写操作的过程出现黑屏,冻屏的过程只是不接收输入和不执行动画,并且会截取屏幕进行显示.
A:系统问题(底层/framework层)
A_1:system_server_watchdog :现象多为卡顿/黑屏
A_2:WMS(WindowManagerService)异常
A_3:surfacefling卡死,表现为定屏(冻屏)
B:app类
B_1: app ANR , 现象多为定屏/黑屏
B_2: app mainThread 阻塞, 没有触发ANR , 现象多为定屏/黑屏
C:SystemUI
C_1:systemUI ANR, 现象为定屏,按power键卡顿
[黑屏定屏那些事 - 系统机制,分析套路和实战(系统篇)](https://blog.csdn.net/feelabclihu/article/details/123911477)
//Android T
stopFreezingScreen(..)解除冻屏
startFreezingScreen(..)执行冻屏
./frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
/** Freeze the screen during a user-switch event. Called by UserController. */
//WMS暴露了一个可以通过binder调用执行的冻屏操作.
@Override
public void startFreezingScreen(int exitAnim, int enterAnim) {
if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
"startFreezingScreen()")) {
throw new SecurityException("Requires FREEZE_SCREEN permission");
}
synchronized (mGlobalLock) {
if (!mClientFreezingScreen) {
mClientFreezingScreen = true;//mClientFreezingScreen变量代表通过客户端进行冻结屏幕
final long origId = Binder.clearCallingIdentity();
try {
startFreezingDisplay(exitAnim, enterAnim);
//CLIENT_FREEZE_TIMEOUT超时的消息,用于结束冻结屏幕,因为客户端不可控,如果冻屏超时,系统会直接解除冻屏
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}
private void doStartFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
int overrideOriginalRotation) {
...
//获取一个WEAK LOCK防止睡眠
mScreenFrozenLock.acquire();
//应用启动电源模式以减少屏幕冻结时间
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
...
//冻结input输入
mInputManagerCallback.freezeInputDispatchingLw();
...
}
Android R :横竖屏切换时更新方向,执行冻屏,截图显示以及计算更新基于新的方向的DisplayInfo和Configuration.
Activity::setRequestedOrientation
->ActivityTaskManagerService::setRequestedOrientation
->ActivityRecord::setRequestedOrientation
->WindowContainer::setOrientation
->WindowContainer::onDescendantOrientationChanged
->DisplayContent::onDescendantOrientationChanged
->DisplayContent::updateOrientation
->DisplayRotation::updateOrientation->DisplayRotation::updateRotationUnchecked
->DisplayRotation::prepareNormalRotationAnimation
->WindowManagerService::startFreezingDisplay
->ScreenRotationAnimation::ScreenRotationAnimation
->ScreenRotationAnimation::setRotation->RotationAnimationUtils::createRotationMatrix
->DisplayContent::updateDisplayAndOrientation
WMS->stopFreezingScreen()冻屏总结
WindowManagerService一个解决界面闪屏的例子
Android系统冻屏、黑屏问题的分析思路
黑屏定屏那些事 - 系统机制,分析套路和实战(系统篇)
定屏/应用卡死问题日志抓取
设备遇到应用卡死并且已经崩溃或者无响应弹框时,查看目录/data/anr/是否有日志生成.
如果目录下没有生成anr日志,界面假死:
adb shell
su
ps -A | grep com.android.launcher3 查看当前app进程id
debuggerd -b 进程id > /sdcard/log_anr.log 打印出包含该进程的所有线程的调用栈信息
Android 开机动画结束后,不显示FallBackHome,(冻屏)几秒后显示Launcher3
冻屏不是黑屏,屏幕看起来像是灰的.
问题:fallbackhome Activity因configChange(uiMode、density、locale变化)频繁启动导致冻屏
解决方案:在AndroidManifest.xml中为fallbackhome配置忽略这些configChanges,以避免影响生命周期
Surface(SurfaceSyncer)同步问题分析
bug:某应用打开后,在点击home键出现黑屏
现象分析:桌面的状态时黑屏的,但是SystemUI的statusBar是显示和功能都是正常的,那一般就是当前app的问题了.
抓日志分析,发现报错: E SurfaceSyncer: Failed to find sync for id=0
黑屏原因:ViewRootImpl.java和SurfaceView 通过SurfaceSyncer产生联系,实现同步更新.
假设同步出现问题,其它流程是正常提交给wms处理,那么Surface没有同步更新会导致图形无法渲染绘制出来,那就黑屏.
Surface同步机制为android13新增,代码位置./frameworks/base/core/java/android/window/SurfaceSyncer.java
,
简介:./frameworks/base/core/java/android/window/SurfaceSyncer.md
.
主要作用为提供ViewRootImpl与SurfaceView(假如窗口存在SurfaceView的情况下)同步服务,实现在主Surface与SurfaceView均完成绘制时,
再去上报至WMS进行窗口状态的切换,避免主Surface绘制完成上报至WMS后、同时SurfaceView迟迟没有绘制完成使得startingWindow过早移除从而产生黑屏的现象.
Android14上保留了Surface同步机制,但是相关代码位置发生变化,不再有SurfaceSyncer.java,
而是移动至SurfaceSyncGroup.java。相关核心方法为createSyncIfNeeded、checkIfSyncIsComplete等。
[093]SurfaceSyncer的致命缺陷
参考:探索Surface同步机制
小二哥
关于Android卡顿掉帧问题
Anroid study ANR / NE(Native exception)
step 1:get events log
adb logcat -b events > e:/log.log
I am_anr : [0,770,com.android.systemui,818462221,executing service com.android.systemui/.wallpapers.ImageWallpaper]
分析:
UserId=0,PID=770,包名=com.android.systemui,flags=...,anr原因:executing service com.android.systemui/.wallpapers.ImageWallpaper
step 2:查看trace文件
上面log打印了ANR的基本信息,具体ANR怎么生成的需要看trace文件,分析代码堆栈调用情况
//main(线程名)、prio(线程优先级,默认是5)、tid(线程唯一标识ID)、Sleeping/Runnable(线程当前状态)
"main" prio=5 tid=1 Runnable
| group="main" sCount=0 ucsCount=0 flags=0 obj=0x722aca68 self=0xb40000764a921be0
| sysTid=943 nice=0 cgrp=default sched=0/0 handle=0x77917e74f8
| state=R schedstat=( 1265523861 1725170986 10361 ) utm=51 stm=75 core=3 HZ=100
| stack=0x7ff7673000-0x7ff7675000 stackSize=8188KB
| held mutexes= "mutator lock"(shared held)
//java 堆栈调用信息(这里可查看导致ANR的代码调用流程)(分析ANR最重要的信息)
at com.android.systemui.util.sensors.ThresholdSensorImpl_Builder_Factory.get(ThresholdSensorImpl_Builder_Factory.java:24)
at com.android.systemui.util.sensors.SensorModule_ProvidePrimaryProximitySensorFactory.get(SensorModule_ProvidePrimaryProximitySensorFactory.java:10)
at com.android.systemui.util.sensors.ProximitySensorImpl_Factory.get(ProximitySensorImpl_Factory.java:123)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0e98eb91> (a dagger.internal.DoubleCheck) // 锁住对象0x0e98eb91
at com.android.systemui.privacy.PrivacyConfig_Factory.get(PrivacyConfig_Factory.java:135)
at com.android.systemui.classifier.FalsingCollectorImpl_Factory.get(FalsingCollectorImpl_Factory.java:38)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0208aaf6> (a dagger.internal.DoubleCheck)
at com.android.systemui.keyguard.dagger.KeyguardModule_NewKeyguardViewMediatorFactory.get(KeyguardModule_NewKeyguardViewMediatorFactory.java:23)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x020beef7> (a dagger.internal.DoubleCheck)
at dagger.internal.DelegateFactory.get(DelegateFactory.java:4)
at com.android.systemui.unfold.UnfoldSharedModule_UnfoldKeyguardVisibilityProviderFactory.get(UnfoldSharedModule_UnfoldKeyguardVisibilityProviderFactory.java:49)
at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager_Factory.get(StatusBarKeyguardViewManager_Factory.java:12)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0bee3164> (a dagger.internal.DoubleCheck)
at dagger.internal.DelegateFactory.get(DelegateFactory.java:4)
at com.android.systemui.keyguard.KeyguardUnlockAnimationController_Factory.get(KeyguardUnlockAnimationController_Factory.java:32)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0ecfafcd> (a dagger.internal.DoubleCheck)
at dagger.internal.DelegateFactory.get(DelegateFactory.java:4)
at com.android.systemui.statusbar.phone.ScrimController_Factory.get(ScrimController_Factory.java:103)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0b8c8082> (a dagger.internal.DoubleCheck)
at com.android.systemui.statusbar.phone.DozeServiceHost_Factory.get(DozeServiceHost_Factory.java:67)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x00868393> (a dagger.internal.DoubleCheck)
at com.android.systemui.keyguard.data.repository.KeyguardRepositoryImpl_Factory.get(KeyguardRepositoryImpl_Factory.java:17)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0c0effd0> (a dagger.internal.DoubleCheck)
at com.android.systemui.keyguard.domain.interactor.KeyguardInteractor_Factory.get(KeyguardInteractor_Factory.java:2)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0f347bc9> (a dagger.internal.DoubleCheck)
at com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor_Factory.get(KeyguardQuickAffordanceInteractor_Factory.java:2)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:13)
- locked <0x0d8622ce> (a dagger.internal.DoubleCheck)
at com.android.systemui.dagger.DaggerReferenceGlobalRootComponent$ReferenceSysUIComponentImpl.injectCustomizationProvider(DaggerReferenceGlobalRootComponent.java:2)
at com.android.systemui.dagger.DaggerReferenceGlobalRootComponent$ReferenceSysUIComponentImpl.inject(DaggerReferenceGlobalRootComponent.java:4)
at java.lang.reflect.Method.invoke(Native method)
at com.android.systemui.SystemUIAppComponentFactoryBase$instantiateProviderCompat$1.onContextAvailable(SystemUIAppComponentFactoryBase.kt:42)
at com.android.systemui.keyguard.CustomizationProvider.attachInfo(CustomizationProvider.kt:7)
at android.app.ActivityThread.installProvider(ActivityThread.java:7514)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:7025)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6796)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2133)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7924)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
补丁:
Traces中显示的线程状态都是C代码定义的,通过查看线程状态对应的信息分析ANR问题
TimedWaiting对应的线程状态是TIMED_WAITING;
kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout 执行了无超时参数的wait函数
kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep() 执行了带有超时参数的sleep函数
ZOMBIE 线程死亡,终止运行
RUNNING/RUNNABLE 线程可运行或正在运行
TIMED_WAIT 执行了带有超时参数的wait、sleep或join函数
MONITOR 线程阻塞,等待获取对象锁
WAIT 执行了无超时参数的wait函数
INITIALIZING 新建,正在初始化,为其分配资源
STARTING 新建,正在启动
NATIVE 正在执行JNI本地函数
VMWAIT 正在等待VM资源
SUSPENDED 线程暂停,通常是由于GC或debug被暂停
Android中ANR的分析和解决
Android内存异常机制(用户空间)_NE
trace详解
Android ANR分析-笔记
Android ANR基本Log分析
Android ANR traces.txt文件分析
Android ANR 2
ANR 分析合集
ANR 问题如何分析
ANR分析套路
我们可以从trace文件[Dumping to /data/anr/anr_2025-01-10-02-00-03-192]中看出,
ANR发生时的某一时刻系统的状态,并且我们可以依据那一刻系统的状态.
并结合adb logcat -b events > d:/event.log
大胆的去想像anr发生时手机在做什么操作.
1.EventLog 看具体的 ANR 时间(搜索 am_anr),看看是否跟 ANR log 能够对上,以确定 ANR Log 是否有效,如果 ANR Log 有效,分析 ANR Log,提取有用信息:pid、tid、死锁等,遇到 ANR 问题,摆在我们面前的 trace 是不是第一案发现场,如果 ANR 发生的输出的信息很多,当时的 CPU 和 I/O 资源比较紧张,那么这段日志输出的时间点可能会延迟 10 秒到 20 秒都有可能,所以我们有时候需要提高警惕。不过正常情况下,EventLog 中的 am_anr 的输出时间是最早的,也是最接近 ANR 时间的。
2.看 MainLog(Android Log) 或者 SystemLog 查看 ANR 详细信息(搜索 ANR in),提取有效的信息。
2.1 发生 ANR 的时间
2.2 打印 ANR 日志的进程
2.3 发生 ANR 的进程
2.4 发生 ANR 的原因
2.5 CPU 负载
2.6 Memory 负载
2.7 CPU 使用统计时间段
2.8 各进程的 CPU 使用率
1.总的 CPU 使用率
2.缺页次数 fault
2.1 xxx minor 表示高速缓存中的缺页次数,可以理解为进程在做内存访问
2.2 xxx major 表示内存的缺页次数,可以理解为进程在做 IO 操作
3. 配合 MainLog(Android Log) 和 EventLog 把 CPU 开始和结束的时间点内的所有有用信息提取出来到一个文件中
3.1 收集关键操作,比如解锁、安装应用、亮灭屏、应用启动等
3.2 收集异常和系统关键 Log
1.系统变慢 :比如 Slow operation、Slow dispatch、Slow delivery、dvm_lock_sample
2.进程变化 :am_kill、am_proc_died、lowmemorykiller、ANR、应用启动关系等
3.系统信息 :cpu info、meminfo、binder info(是否满了) 、iowait (是否过高)
3.3 收集 ANR 进程的所有关键线程的运行情况、线程优先级等
3.4 上面收集的信息,分析进一步理出系统当时的情况、状态 ,比如
1.是处于低内存频繁杀进程?
2.重启第一次解锁系统繁忙
3.还是短时间内多个应用启动系统繁忙
4.还是应用自己的逻辑等待?
4.不行就加 Log 复现.
anr类型
看trace,主线程 "main" prio=5 tid=1 Runnable
其实Input dispatching timed out有两种类型,
Input dispatching timed out (Application does not have a focused window)
//多发主线程耗时操作,导致input事件无法分发被响应 ,关键字:WindowManager,InputDispatcher,timeout, Looper
Input dispatching timed out(server) is not responding. Waited 5002ms for FocusEvent(hasFocus=false)
一个是input没有焦点窗口类型的ANR,一个是input有焦点窗口时的ANR,这两个类型是不一样的
关键字
anr相关:ANR in 、am_anr、WindowManager: ANR in ActivityRecord
wm生命周期:wm_set_resumed_activity、wm_on_resume_called、Zygote : Forked child process、WindowManager: finishDrawing、am_kill : [0,23853,wp.wattpad,0,bg anr]、
输入事件下发:InputDispatcher
焦点:input_focus
耗时相关:CheckTime、Choreographer、OpenGLRenderer、
低内存杀进程:lowmemorykiller、killinfo
案例演示:
1.UI线程被阻塞:可能是因为在主线程(UI线程)上执行了耗时的操作,如网络请求、大量计算或数据库操作.
解决1: 异步处理,耗时线程放在后台
ANR-实例分析-主线程(UI线程)上执行了耗时的操作
2.io 负载高...
ANR-实例分析-负载过高
Android anr问题分析
InputDispatcher无焦点窗口ANR问题
ANR实例 -- 死锁
死锁是一种线程同步问题,指两个或多个线程在执行过程中由于相互等待对方释放资源,导致它们永远无法继续执行下去的现象.
一般死锁满足下面四个条件:
- 互斥条件:一个资源每次只能被一个线程占用。
- 持有并等待条件:一个线程已经持有至少一个资源,同时又请求新的资源,但该资源已被其他线程占用。
- 不可剥夺条件:线程已获得的资源在未使用完成之前,不能被其他线程强行剥夺。
- 循环等待条件:存在一个线程等待链,链中每个线程都在等待下一个线程所持有的资源。
死锁trace 日志分析:
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x731e4920 self=0xb40000716ca3bc00
| sysTid=2298 nice=0 cgrp=foreground sched=1073741824/0 handle=0x721ae71d20
| state=S schedstat=( 7416519353172 1278618376091 9259320 ) utm=674457 stm=67194 core=7 HZ=100
| stack=0x7fd9a08000-0x7fd9a0a000 stackSize=8188KB
| held mutexes=
at android.e.b(unavailable:0)
- waiting to lock <0x0bef5c87> (a w7.a) held by thread 13
at J6.b.onTransact(unavailable:214)
at android.os.Binder.execTransactInternal(Binder.java:1505)
at android.os.Binder.execTransact(Binder.java:1444)
at android.os.BinderProxy.transactNative(Native method)
at android.os.BinderProxy.transact(BinderProxy.java:586)
at android.e.f(unavailable:59)
at android.d.onServiceConnected(unavailable:35)
at android.a.onServiceConnected(unavailable:29)
- locked <0x0a85b7b4> (a java.util.Collections$SynchronizedSet)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:2256)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:2289)
at android.os.Handler.handleCallback(Handler.java:959)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loopOnce(Looper.java:242)
at android.os.Looper.loop(Looper.java:327)
at android.app.ActivityThread.main(ActivityThread.java:8843)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:617)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1002)
DumpLatencyMs: 87.2616
"BI_Common_SDK_workThread" prio=5 tid=13 Blocked
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x1403cfb0 self=0xb400007131cd6400
| sysTid=2337 nice=0 cgrp=foreground sched=1073741824/0 handle=0android0d07b9770
| state=S schedstat=( 1894403086 2330218128 4644 ) utm=70 stm=118 core=5 HZ=100
| stack=0android0d06b6000-0android0d06b8000 stackSize=1037KB
| held mutexes=
at android.c.O(unavailable:47)
- waiting to lock <0x0a85b7b4> (a java.util.Collections$SynchronizedSet) held by thread 1
- locked <@addr=0x141ff608> (a android.c)
at android.e.g(unavailable:26)
at android.e.a(unavailable:16)
- locked <0x0bef5c87> (a w7.a)
at androidx.compose.ui.platform.b.l(unavailable:88)
- locked <@addr=0x141fe7d0> (a y7.f)
at com.android.example.reportData(unavailable:42)
at com.android.example.reportDataFrom(unavailable:31)
atcom.android.example.access$1400(unavailable:0)
at com.android.example$3.run(unavailable:74)
"main" prio=5 tid=1 Blocked
,表示主线程阻塞,等待获取对象锁
waiting to lock <0x0bef5c87> (a w7.a) held by thread 13
,主线程再等待标识是0x0bef5c87
的锁,这个锁被线程13持有.
locked <0x0a85b7b4> (a java.util.Collections$SynchronizedSet)
,表示主线程持有的是标识为0x0a85b7b4
的锁
上述分析可得标识0x0bef5c87
被线程13持有,需要找线程13的锁.需要从trace中找到tid=13的日志.
"BI_Common_SDK_workThread" prio=5 tid=13 Blocked
,显示状态也为Blocked,说明这个线程也被阻塞了.
waiting to lock <0x0a85b7b4> (a java.util.Collections$SynchronizedSet) held by thread 1
,
这里的锁标识为0x0a85b7b4
,它再等待这个锁,但是这个锁被线程1 持有.
- locked <0x0bef5c87> (a w7.a)
线程13持有的标识为0x0bef5c87的锁,正好是线程1等待的锁
因此,这里是一个死锁导致了ANR的现象.
接着分析,找出导致死锁的代码部分:
- locked <0x0bef5c87> (a w7.a)
at androidx.compose.ui.platform.b.l(unavailable:88)
- locked <@addr=0x141fe7d0> (a y7.f)
at com.android.example.reportData(unavailable:42)
Android中常见的死锁场景:
- 多线程的嵌套锁定:当两个线程试图以不同顺序锁定资源时,就会导致死锁。
- 主线程阻塞:Android 中主线程(UI 线程)被用于更新界面。如果在主线程中使用了同步锁(如 synchronized),而该锁被另一个工作线程持有,同时工作线程也需要主线程完成某些任务,可能导致死锁。
- 跨线程通信死锁:例如,一个线程等待 Handler 发送的消息,而 Handler 本身在另一个线程中被阻塞。
如何避免死锁:
主线程避免长时间锁定:避免在主线程中执行耗时操作,尤其是加锁操作。耗时任务应交由后台线程处理。
减少锁的持有时间:尽可能缩小锁的作用范围,避免长时间持有锁。
避免嵌套锁定:避免在一个锁内请求另一个锁,简化锁的获取逻辑。 如果必须嵌套锁定,应确保所有线程以相同的顺序获取锁。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
2022-08-23 模仿安卓系统原生的亮度调节框 --->Android弹出框