Android显示系统之SurfaceFlinger(一)

2024-04-22 17:32

本文主要是介绍Android显示系统之SurfaceFlinger(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.1 SurfaceFlinger的启动

SurfaceFlinger的启动和ServiceManager有点类似,它们都属于系统的底层支撑服务,必需在设备开机的早期就运行起来。

/*frameworks/base/cmds/system_server/library/System_init.cpp*/

extern "C" status_t  system_init()

{…   

   property_get("system_init.startsurfaceflinger", propBuf,"1");

    if (strcmp(propBuf,"1") == 0) {

       SurfaceFlinger::instantiate();

}…

这个System_init.cpp会被编译到libsystem_server库中,然后由SystemServer在JNI层进行加载调用,从而启动包括SurfaceFlinger、SensorService等在内的系统服务。

和AudioFlinger/AudioPolicyService看到的情况一样,它调用instantiate来创建一个binder server,名称为“SurfaceFlinger”。而且强指针的特性让它在第一次被引用时会调用onFirstRef:

void SurfaceFlinger::onFirstRef()

{

    mEventQueue.init(this);

   run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);

    mReadyToRunBarrier.wait();

}

成员变量mEventQueue是一个MessageQueue,我们在进程章节已经详细分析过它与Looper、Handler等类的使用,大家可以先回头参考下(虽然Java层的这些类与SurfaceFlinger中用到的有一定差异,但其本质原理是一样的)。既然有消息队列,那就一定会有配套的事件处理器Handler以及循环体Looper,这些是在MessageQueue::init函数中创建的,即:

/*frameworks/native/services/surfaceflinger/MessageQueue.cpp*/

void MessageQueue::init(const sp<SurfaceFlinger>& flinger)

{

    mFlinger = flinger;

    mLooper = newLooper(true);

    mHandler = newHandler(*this);

}

也就是说这个MessageQueue类不但提供了消息队列,其内部还囊括了消息的处理机制,可以说是个“大杂烩”。那么这个Looper会在什么时候运行起来呢?显然SurfaceFlinger需要先自行创建一个新的线程来承载这一“业务”,否则就会阻塞SystemServer的主线程,这一点和AudioFlinger有一定差异。函数最后的mReadyToRunBarrier.wait()也可以证明这一点——mReadyToRunBarrier在等待一个事件,在事件没有发生前其所在的线程就会处于等待状态。这是Android系统里两个线程间的一种典型交互方式。举个例子来说,A线程将启动B线程,并且A接下来的工作会依赖于B进行。换句话说,A必顺要等到B说“好了,我已经ok”了,它才能继续往下走,否则就会出错。由此可见,SurfaceFlinger新启动的这个线程中一定还会调用mReadyToRunBarrier。

这样我们也能推断出SurfaceFlinger一定是继承自Thread线程类的,如下所示:

class SurfaceFlinger :

        publicBinderService<SurfaceFlinger>,

        …

        protected Thread

 

所以上面代码中可以调用Thread::run()方法,进而启动一个名为“SurfaceFlinger”的线程,优先级别为PRIORITY_URGENT_DISPLAY。这个优先级是在ThreadDefs.h中定义的,如下表所示:

表格 11‑1 Android系统的线程优先级定义

Priority

Value

Description

ANDROID_PRIORITY_LOWEST

19

可以使用最后的

ANDROID_PRIORITY_BACKGROUND

10

用于background tasks

ANDROID_PRIORITY_NORMAL

0

大部分线程都以这个优先级运行

ANDROID_PRIORITY_FOREGROUND

-2

用户正在交互的线程

ANDROID_PRIORITY_DISPLAY

-4

UI主线程

ANDROID_PRIORITY_URGENT_DISPLAY

-8

这个值由HAL_PRIORITY_URGENT_DISPLAY来指定,当前版本中是-8。只在部分紧急状态下使用

ANDROID_PRIORITY_AUDIO

-16

正常情况下的声音线程

ANDROID_PRIORITY_URGENT_AUDIO

-19

声音线程(通常情况不用)

ANDROID_PRIORITY_HIGHEST

-20

最高优先级,禁止使用

ANDROID_PRIORITY_DEFAULT

0

默认情况下就是ANDROID_PRIORITY_NORMAL

ANDROID_PRIORITY_MORE_FAVORABLE

-1

在上述优先级的基础上,用于加大优先级

ANDROID_PRIORITY_LESS_FAVORABLE

+1

在上述优先级的基础上,用于减小优先级

 

数值越大的,优先级越小。因为各等级间的数值并不是连续的,我们可以通过ANDROID_PRIORITY_MORE_FAVORABLE(-1)来适当地提高优先级,或者是利用ANDROID_PRIORITY_LESS_FAVORABLE(+1)来降低优先级。

由此可见,SurfaceFlinger工作线程所采用的优先级是相对较高的。这样做是必然的,因为屏幕显示无疑是人机交互中最直观的用户体验,任何滞后的响应速度都将大大降低产品的吸引力。

在执行了run()以后,Thread会自动调用threadLoop()接口,即:

bool SurfaceFlinger::threadLoop()

{

    waitForEvent();

    return true;

}

相当简洁的两句话,所有SurfaceFlinger接下来要执行的工作都涵括在这里了。其中waitForEvent()是SurfaceFlinger中的成员函数,它进一步调用mEventQueue.waitMessage():

void MessageQueue::waitMessage() {

    do {

       IPCThreadState::self()->flushCommands();

        int32_t ret =mLooper->pollOnce(-1);

        switch (ret) {

            caseALOOPER_POLL_WAKE:

            caseALOOPER_POLL_CALLBACK:

                continue;

            caseALOOPER_POLL_ERROR:

               ALOGE("ALOOPER_POLL_ERROR");

            caseALOOPER_POLL_TIMEOUT:

                // timeout (should not happen)

                continue;

            default:

                // should nothappen

               ALOGE("Looper::pollOnce() returned unknown status %d", ret);

                continue;

        }

    } while (true);

}

可以看到程序在这里进入了一个死循环,而且即便pollOnce的执行结果是ALOOPER_POLL_TIMEOUT,也同样不会跳出循环。这是Android系统在对待系统级严重错误时的一种普遍态度——一旦发生,就没救了,听天由命吧。。。

下面这句将在内部调用MessageQueue::mHandler来处理消息:

mLooper->pollOnce(-1);

这样子就构建了一个简洁而又完整的循环消息处理框架,SurfaceFlinger就是基于这个框架完成来自系统中各个程序的显示请求的。大家可能会有疑问,mHandler是由MessageQueue直接通过new Handler()生成的,这样的话如何能处理特定的SurfaceFlinger消息请求呢?个人感觉有这个困惑是由于Handler类取名不当引起的。实际上此Handler并非我们经常看到的那个Handler,这里的Handler是MessageQueue中自定义的一个事件处理器,也就是说它是专门为SurfaceFlinger设计的。

/*frameworks/native/services/surfaceflinger/MessageQueue.cpp*/

void MessageQueue::Handler::handleMessage(const Message&message) {

    switch (message.what) {

        case INVALIDATE:

           android_atomic_and(~eventMaskInvalidate, &mEventMask);

           mQueue.mFlinger->onMessageReceived(message.what);

            break;

        case REFRESH:

           android_atomic_and(~eventMaskRefresh, &mEventMask);

           mQueue.mFlinger->onMessageReceived(message.what);

            break;

    }

}

如上代码段所示,mHandler当收到INVALIDATE和REFRESH请求时,进一步回调了SurfaceFlinger中的onMessageReceived。等于是绕了一个大圈,又回到SurfaceFlinger中了——这或许也是我们在分析Android源码时经常碰到的情况J。

不过我们到目前为止还是没看到SurfaceFlinger是如何通知SystemServer线程解除等待的。这个工作是在下面的函数完成的:

status_t SurfaceFlinger::readyToRun()

{…

    mReadyToRunBarrier.open();//好了,现在可以解禁线程A了

}

函数readyToRun是在一个线程进入run循环前调用的,它为SurfaceFlinger的正常工作提供了各种必要的基础。我们在后续小节还会看到其中的更多内容,这里先分析与消息处理有关的部分。前面所说的mReadyToRunBarrier果然在这里又被调用了,open()是告诉所有正在等待的线程可以继续运行了。Barrier类内部实际上也是使用了Condition::broadcast()、Condition::wait()等常规互斥方法,只是加了一层封装而已。


这篇关于Android显示系统之SurfaceFlinger(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/926409

相关文章

linux系统中java的cacerts的优先级详解

《linux系统中java的cacerts的优先级详解》文章讲解了Java信任库(cacerts)的优先级与管理方式,指出JDK自带的cacerts默认优先级更高,系统级cacerts需手动同步或显式... 目录Java 默认使用哪个?如何检查当前使用的信任库?简要了解Java的信任库总结了解 Java 信

C#实现SHP文件读取与地图显示的完整教程

《C#实现SHP文件读取与地图显示的完整教程》在地理信息系统(GIS)开发中,SHP文件是一种常见的矢量数据格式,本文将详细介绍如何使用C#读取SHP文件并实现地图显示功能,包括坐标转换、图形渲染、平... 目录概述功能特点核心代码解析1. 文件读取与初始化2. 坐标转换3. 图形绘制4. 地图交互功能缩放

Oracle数据库在windows系统上重启步骤

《Oracle数据库在windows系统上重启步骤》有时候在服务中重启了oracle之后,数据库并不能正常访问,下面:本文主要介绍Oracle数据库在windows系统上重启的相关资料,文中通过代... oracle数据库在Windows上重启的方法我这里是使用oracle自带的sqlplus工具实现的方

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

在Android中使用WebView在线查看PDF文件的方法示例

《在Android中使用WebView在线查看PDF文件的方法示例》在Android应用开发中,有时我们需要在客户端展示PDF文件,以便用户可以阅读或交互,:本文主要介绍在Android中使用We... 目录简介:1. WebView组件介绍2. 在androidManifest.XML中添加Interne

JWT + 拦截器实现无状态登录系统

《JWT+拦截器实现无状态登录系统》JWT(JSONWebToken)提供了一种无状态的解决方案:用户登录后,服务器返回一个Token,后续请求携带该Token即可完成身份验证,无需服务器存储会话... 目录✅ 引言 一、JWT 是什么? 二、技术选型 三、项目结构 四、核心代码实现4.1 添加依赖(pom

基于Python实现自动化邮件发送系统的完整指南

《基于Python实现自动化邮件发送系统的完整指南》在现代软件开发和自动化流程中,邮件通知是一个常见且实用的功能,无论是用于发送报告、告警信息还是用户提醒,通过Python实现自动化的邮件发送功能都能... 目录一、前言:二、项目概述三、配置文件 `.env` 解析四、代码结构解析1. 导入模块2. 加载环

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux查询服务器系统版本号的多种方法

《Linux查询服务器系统版本号的多种方法》在Linux系统管理和维护工作中,了解当前操作系统的版本信息是最基础也是最重要的操作之一,系统版本不仅关系到软件兼容性、安全更新策略,还直接影响到故障排查和... 目录一、引言:系统版本查询的重要性二、基础命令解析:cat /etc/Centos-release详