结合源码讲解下Android中的截图流程

embedded/2024/11/18 14:29:00/

在Android中,截图过程主要涉及以下几个关键步骤:

  1. 捕获屏幕内容
  2. 生成Bitmap图像
  3. 将Bitmap传递给用户应用
    在系统内部,截图流程涉及Android Framework、SurfaceFlinger和Gralloc等模块的协作。下面详细介绍这几个步骤的实现过程,并结合源码解释。
    在这里插入图片描述

1. 触发截图请求

通常,截图是通过以下几种方式触发的:

  • 用户按下系统快捷键(如电源键 + 音量下键)来触发截图
  • 应用程序通过MediaProjection API来发起录屏或截屏
  • 系统调用SurfaceControl.screenshot方法实现
    常见的截图调用方式是在系统界面中调用TakeScreenshotService,此时系统服务会调用截图的核心代码。
// SystemUI ScreenshotController.java
public void takeScreenshot() {// 调用系统服务的截图请求mScreenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN,true /* statusBarVisible */,true /* navBarVisible */,mHandler,new Consumer() { ... });
}

2. 截图流程核心方法:SurfaceControl.screenshot

在系统内部,截图操作最终会调用SurfaceControl.screenshot方法,该方法会通过JNI与Native层进行交互,最终调用到SurfaceFlinger服务来生成截图。

// SurfaceControl.java
public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {return nativeScreenshot(sourceCrop, width, height, rotation);
}

nativeScreenshot方法中,它会通过JNI调用到SurfaceControl.cppnativeScreenshot函数:

// SurfaceControl.cpp
static jobject nativeScreenshot(JNIEnv* env, jobject clazz, ...)
{sp display = SurfaceComposerClient::getInternalDisplayToken();sp buffer;SurfaceComposerClient::screenshot(display, buffer, ...);return android::Bitmap_createFromGraphicBuffer(env, buffer);
}

3. SurfaceFlinger生成图像数据

SurfaceComposerClient::screenshot方法中,会通过IPC调用SurfaceFlinger的截图方法。

// SurfaceComposerClient.cpp
status_t SurfaceComposerClient::screenshot(const sp& display,sp& outBuffer, ...)
{return ScreenshotClient::capture(display, outBuffer, ...);
}

ScreenshotClient::capture会使用GraphicBuffer从显示设备捕获屏幕数据。

// ScreenshotClient.cpp
status_t ScreenshotClient::capture(const sp& display,sp& outBuffer, ...)
{// 通过Binder IPC调用SurfaceFlinger服务return composer->captureScreen(display, outBuffer, ...);
}

4. SurfaceFlinger服务生成截图

在SurfaceFlinger服务的captureScreen方法中,系统会通过OpenGL ES或硬件加速器来渲染和捕获屏幕内容。SurfaceFlinger会将当前显示内容复制到GraphicBuffer中,最终生成一个Bitmap对象。

// SurfaceFlinger.cpp
status_t SurfaceFlinger::captureScreen(const sp& display,sp& buffer, ...)
{// 调用OpenGL渲染屏幕内容renderScreenToBuffer(buffer, ...);
}

SurfaceFlinger生成截图的流程包含从多个图层合成最终图像并渲染到GraphicBuffer。我们深入解读其主要实现逻辑和关键源码。

SurfaceFlinger生成截图的核心方法是captureScreen。它通过以下几个步骤生成屏幕截图:

(1). 选择合适的显示设备
(2). 准备目标缓冲区(GraphicBuffer)
(3). 合成图层内容到缓冲区
(4). 将缓冲区数据保存为图像

Step 1: 选择显示设备

SurfaceFlinger服务通常有多个显示设备,例如内置屏幕、外接显示器等。它首先获取用户指定的显示设备或默认的主显示设备。

// SurfaceFlinger.cpp
status_t SurfaceFlinger::captureScreen(const sp& display,sp& buffer, ...)
{// 获取指定的显示设备,或使用默认的内置显示sp hw = getDisplayDeviceLocked(display);if (hw == nullptr) {return NAME_NOT_FOUND;}...
}

getDisplayDeviceLocked会根据IBinder标识符找到对应的显示设备。每个显示设备都包含了显示的配置信息、分辨率、旋转角度等。

Step 2: 准备目标缓冲区(GraphicBuffer)

在确定了显示设备后,SurfaceFlinger会创建一个GraphicBuffer,用于保存截图内容。GraphicBuffer是一个共享内存区域,可以在不同的进程间传递。

// SurfaceFlinger.cpp
buffer = new GraphicBuffer(reqWidth, reqHeight, PIXEL_FORMAT_RGBA_8888,GraphicBuffer::USAGE_HW_TEXTURE |GraphicBuffer::USAGE_HW_RENDER);

在这里,GraphicBuffer初始化时指定了缓冲区的宽度、高度和格式,PIXEL_FORMAT_RGBA_8888代表每个像素点包含红、绿、蓝、透明度四个通道,各占8位。

Step 3: 合成图层内容到缓冲区

接下来,SurfaceFlinger会将所有图层内容绘制到缓冲区中。这是最复杂的部分,主要使用OpenGL ES进行渲染。

SurfaceFlinger会调用renderScreenToBuffer方法,该方法会依次将所有显示的图层(例如应用窗口、状态栏等)合成为一个最终图像。

// SurfaceFlinger.cpp
status_t SurfaceFlinger::renderScreenToBuffer(const sp& hw,const sp& buffer, ...)
{// 设置OpenGL的绘图目标为新创建的GraphicBufferRenderEngine* engine = getRenderEngine();engine->bindExternalTextureBuffer(buffer->handle, ...);// 遍历每个图层,依次绘制for (const auto& layer : hw->getVisibleLayersSortedByZ()) {// 绘制图层内容layer->draw(engine);}engine->unbindExternalTextureBuffer();return NO_ERROR;
}

这里的关键在于:

  • 使用OpenGL的FBO(帧缓冲区对象)来将绘图目标切换到GraphicBuffer
  • 遍历每个图层,并调用layer->draw(engine)将图层内容绘制到缓冲区
图层合成过程

每个图层都包含图像内容和图层属性(如位置、大小、透明度、旋转等)。在draw方法中,SurfaceFlinger通过OpenGL的纹理映射和着色器,将每个图层的内容合成到目标缓冲区。

// Layer.cpp
void Layer::draw(RenderEngine& engine) {// 使用OpenGL纹理和着色器绘制图层内容engine.drawTexture(mTexture, mPosition, mSize, mAlpha, mMatrix);
}
Step 4: 将缓冲区数据保存为图像

完成合成后,缓冲区中的内容即为整个屏幕的图像。最终,SurfaceFlinger会将该GraphicBuffer返回给调用者,并将其转化为一个Bitmap对象供Java层使用。

// ScreenshotClient.cpp
sp buffer;
SurfaceFlinger::captureScreen(display, buffer, ...);// 在Java层创建Bitmap
jobject bitmap = android::Bitmap_createFromGraphicBuffer(env, buffer);

综上, SurfaceFlinger的截图生成过程:
(1). 选择合适的显示设备 获取需要截图的显示设备
(2). 准备目标缓冲区(GraphicBuffer):使用GraphicBuffer为目标图像分配存储空间
(3). 合成图层内容到缓冲区:使用OpenGL渲染所有图层,将内容绘制到缓冲区
(4). 将缓冲区数据保存为图像:将缓冲区内容转换为Bitmap

5. 将Bitmap返回给应用

在Native层生成Bitmap后,会通过JNI将Bitmap传递回Java层,最终在用户应用中可访问该Bitmap。

// SurfaceControl.java
public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {return nativeScreenshot(sourceCrop, width, height, rotation);
}

总结

  • 触发截图请求:系统通过快捷键或API触发截图
  • 调用截图方法:SurfaceControl.screenshot -> SurfaceFlinger.captureScreen
  • 生成屏幕内容:SurfaceFlinger通过OpenGL渲染当前显示内容到GraphicBuffer
  • 返回Bitmap:将生成的Bitmap对象传递给应用

http://www.ppmy.cn/embedded/138562.html

相关文章

2024山西省网络建设运维第十八届职业院校技能大赛解析答案(7. mariadb 服务)

\7. mariadb 服务 任务描述:请安装 mariadb 服务,建立数据表,具体要求如下: (1)配置 linux5 为 mariadb 服务器,创建数据库用户 xiao,在任意机 器上对所有数据库有完全权限。 (2)配置linux6为mariadb客户端,使用数据库用户xiao远程登录mariadb 服务器,创建数据库 userd…

探索C/C++的奥秘之vector

vector<int>是一个类模板&#xff0c;要显示的实例化&#xff0c;并且vector<char>不能代替string。原因&#xff1a; 1.string和vector<char>再结构上有所不同&#xff0c;string要保证末尾有\0&#xff0c;sting就是自动生成的\0&#xff0c;vector<ch…

POI和easyExcel的讲解和使用

目录 应用场景&#xff1a; Apache POI&#xff1a; EasyExcel&#xff1a; POI具体实现&#xff1a; POI-Excel 写&#xff1a; POI-Excel 读&#xff1a; 大文件写HSSF&#xff1a; 大文件写XSSF&#xff1a; 大文件写SXSSF&#xff1a; EasyExcel&#xff1a; 导…

【GeekBand】C++设计模式笔记11_Builder_构建器

1. “对象创建” 模式 通过 “对象创建” 模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&#xff09;&#xff0c;从而支持对象创建的稳定。它是接口抽象之后的第一步工作。典型模式 Factory MethodAbstract …

生成式GPT商品推荐:精准满足用户需求

生成式GPT商品推荐&#xff1a;精准满足用户需求 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;电商平台正在逐步迎来一场前所未有的变革。尤其是生成式GPT&#xff08;Generative Pre-trained Transformer&#xff09;技术的应用&#xff0c;正在重新定…

使用真实 Elasticsearch 进行更快的集成测试

作者&#xff1a;来自 Elastic Piotr Przybyl 了解如何使用各种数据初始化和性能改进技术加快 Elasticsearch 的自动化集成测试速度。 在本系列的第 1 部分中&#xff0c;我们探讨了如何编写集成测试&#xff0c;让我们能够在真实的 Elasticsearch 环境中测试软件&#xff0c;并…

深度学习与飞桨 PaddlePaddle Fluid

编辑推荐 飞桨PaddlePaddle是百度推出的深度学习框架&#xff0c;不仅支撑了百度公司的很多业务和应用&#xff0c;而且随着其开源过程的推进&#xff0c;在其他行业得到普及和应用。 本书基于2019年7月4日发布的飞桨PaddlePaddle Fluid 1.5版本&#xff08;后续版本会兼容旧版…

面试编程题目(一)细菌总数计算

题目如图&#xff1a; 第一题&#xff1a; import lombok.AllArgsConstructor; import lombok.Data;import java.util.Arrays; import java.util.Collections; import java.util.List;/*** description: 细菌实体类* author: zhangmy* Version: 1.0* create: 2021-03-30 11:2…