在Android系统开发过程中,偶尔会遇到显示异常的问题,如画面不再刷新,也就是冻屏,或者屏幕变黑,如果对图形显示系统不熟悉,遇到此类问题还是比较棘手的。如果在工作中遇到类似的问题,或者对图形显示系统感兴趣,可参考清华大学出版社出版的《Android图形显示系统》。 图形显示系统是Android系统中最为核心、复杂的子系统,掌握它对于理解Android系统会有一个很大的提升,是一门进阶课题。

下面主要结合《Android图形显示系统》来分析工作过程中碰到的图形显示异常的问题。该书主要基于Android9,而本文主要基于Android12,其实思路是一样的,只不过在代码上有差异。

假设遇到了一个屏幕突然变黑的问题,首先得确认黑屏的可能原因,比如是否灭屏,如果不是灭屏,那就是应用的图形内容没有正常地传递到屏幕,可以在SurfaceFlinger确定大体原因。

通过《Android图形显示系统》可以指导,应用的图形内容要经过SurfaceFlinger才能传递到显示设备,SurfaceFlinger每隔一定时间(间隔由帧率决定)会向屏幕传递图形数据,但是并不是每个应用的图形都会显示,只有最前面应用的画面才能显示,应用的图形数据一般保存在图层中,因此每次刷新都会收集可见的图层。核心流程如下。

void SurfaceFlinger::onMessageRefresh() {

mCompositionEngine->present(refreshArgs);

}

void CompositionEngine::present(CompositionRefreshArgs& args) {

for (const auto& output : args.outputs) {

output->prepare(args, latchedLayers);

}

}

void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,

LayerFESet& geomSnapshots) {

rebuildLayerStacks(refreshArgs, geomSnapshots);

}

void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,

compositionengine::Output::CoverageState& coverage) {

for (auto layer : reversed(refreshArgs.layers)) {

ensureOutputLayerIfVisible(layer, coverage);

}

}

void Output::ensureOutputLayerIfVisible(sp& layerFE,

compositionengine::Output::CoverageState& coverage) {

auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);

layerFE->dumpLayer();

}

流程解析如下:

1)SurfaceFlinger在处理onMessageRefresh时正是要刷新屏幕,调用CompositionEngine的present进行下一步处理。

2)CompositionEngine在处理present时,针对每个显示设备调用prepare先准备好要处理的图层,每个Output对象代表一个显示设备。

3)Output在处理prepare主要重建图层栈,也就是找到可见的图层,如果上面的流程走到ensureOutputLayerIfVisible的ensureOutputLayer,可以确定该图层是可见的,这里可以把该图层及其父图层打印出来,这里layerFE->dumpLayer();是新加的方法,需要在Layer也加上相应的实现,如下。

class LayerFE : public virtual RefBase {

virtual void dumpLayer() = 0;

};

class Layer : public virtual RefBase, compositionengine::LayerFE {

void dumpLayer() override {

sp parent = getParent();

if(parent != nullptr){

parent->dumpLayer();

}

ALOGI("dumpLayer LayerName: %s", mName.c_str());

}

}

这里会把图层及其父图层的名称打印出来,结果如下。

04-12 14:37:57.714 720 720 I BufferLayer: dumpLayer LayerName: WindowToken{7c850a1 android.os.BinderProxy@bd7a6ab}#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: f541552 ScreenDecorOverlayBottom#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: ScreenDecorOverlayBottom#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowToken{6e71384 android.os.BinderProxy@26cb1d8}#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: fdb4f1c ScreenDecorOverlay#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: ScreenDecorOverlay#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Root#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowedMagnification:0:31#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Leaf:24:25#0

04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowToken{4b66858 andr