RK3188作为一款经典的嵌入式处理器,在智能终端设备中广泛应用。Android 5.1系统通过双屏异显补丁可以实现主副屏独立显示不同内容的功能,这在商显、教育等场景特别实用。我遇到过不少开发者反馈,在设备运行一段时间后,副屏会出现背光亮但无图像信号的异常情况。
这种问题通常表现为:设备刚启动时双屏显示正常,但在安装多个应用后,副屏突然"罢工"。通过adb shell dumpsys display命令查看显示状态时,会发现副屏的mOverrideDisplayInfo中state显示为UNKNOWN而非正常的ON状态。这种情况往往与系统服务间的状态同步机制有关,需要从DisplayManagerService、WindowManagerService等核心模块的交互流程入手分析。
排查这类问题首先要学会看关键日志。正常状态下,dumpsys display的输出中两个屏幕的state都应该显示为ON:
bash复制Display 0:
mBaseDisplayInfo=...state ON...
mOverrideDisplayInfo=...state ON...
Display 1:
mBaseDisplayInfo=...state ON...
mOverrideDisplayInfo=...state ON...
异常情况下,副屏(Display 1)的mOverrideDisplayInfo会显示UNKNOWN状态。这个状态值来自WindowManagerService对显示状态的覆写,说明系统窗口管理器没有正确更新副屏状态。
通过对比正常和异常场景的logcat,我发现关键差异在于缺少这条日志:
code复制V/ActivityManager: Display added displayId=1
Android显示系统的状态传递是个复杂的过程。核心流程包括:
在RK3188平台上,问题出在第3步:由于主副屏初始化间隔太短(约30ms),当ActivityStackSupervisor注册监听时,两个显示设备都已经初始化完成,导致后续的onDisplayAdded回调没有被触发。
通过分析SystemServer的启动流程,发现DisplayManagerService在startBootstrapServices阶段就启动了,而ActivityStackSupervisor的setWindowManager调用要等到startOtherServices阶段:
java复制// SystemServer.java
private void startBootstrapServices() {
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
}
private void startOtherServices() {
mActivityManagerService.setWindowManager(wm);
}
这导致了一个时间差:DisplayManagerService已经完成了双屏初始化,但ActivityStackSupervisor还没有注册显示监听。更糟的是,LocalDisplayAdapter在registerLocked()中直接尝试连接了所有显示设备:
java复制// LocalDisplayAdapter.java
public void registerLocked() {
for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
tryConnectDisplayLocked(builtInDisplayId); // 立即尝试连接主副屏
}
}
当副屏初始化完成后,由于没有监听者注册,系统错过了发送DISPLAY_DEVICE_EVENT_ADDED事件的时机。这导致:
这就是为什么副屏只有背光没有图像信号的根本原因。
修改LocalDisplayAdapter的初始化逻辑,将副屏连接推迟到系统准备就绪后:
java复制// LocalDisplayAdapter.java
@Override
public void registerLocked() {
super.registerLocked();
// 只初始化主屏
tryConnectDisplayLocked(SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
}
public void onSystemReady(){
new Thread(){
public void run(){
int retry = 0;
// 重试机制连接副屏
while(mDevices.get(SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI) == null){
tryConnectDisplayLocked(SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI);
if(retry++ > 5) break;
Thread.sleep(1000);
}
// 注册热插拔监听
mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
}
}.start();
}
在系统准备就绪时触发副屏初始化:
java复制// DisplayManagerService.java
public void systemReady(boolean safeMode, boolean onlyCore) {
((LocalDisplayAdapter)mDisplayAdapters.get(0)).onSystemReady();
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
}
这种修改带来了三个好处:
验证修复效果时,建议关注以下关键点:
code复制D/LocalDisplayAdapter: tryConnectDisplayLocked 1
V/ActivityManager: Display added displayId=1
在实际项目中,还可以进一步优化:
我在一个商显项目中实测发现,通过增加状态补偿机制,可以将副屏恢复时间从原来的5-10秒缩短到1秒以内。关键是在DisplayManagerService中添加如下逻辑:
java复制private void checkDisplayState() {
synchronized (mSyncRoot) {
for (LogicalDisplay display : mLogicalDisplays.values()) {
if(display.getDisplayInfoLocked().state != Display.STATE_ON) {
sendDisplayEventLocked(display.getDisplayId(),
DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
}
}
}
}
这个案例展示了Android显示系统各服务间复杂的交互关系。通过调整初始化时序和增加状态同步机制,我们成功解决了副屏状态异常的问题。在实际开发中,理解系统服务的启动顺序和状态传递链路,往往能帮助我们快速定位这类疑难问题。