在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
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
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