自学内容网 自学内容网

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

在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的绘图目标为新创建的GraphicBuffer
    RenderEngine* 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对象传递给应用

原文地址:https://blog.csdn.net/wudexiaoade2008/article/details/143753044

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!