Android RecyclerView+Coil解码Bitmap设置进View,RenderThread上屏显示Graphics

Android RecyclerView+Coil解码Bitmap设置进View,RenderThread上屏显示Graphics

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                         RecyclerView 列表页                         │
└─────────────────────────────────────────────────────────────────────┘

        数据源
          │
          │  (content:// / file / uri)
          ▼
┌──────────────────────┐
│   Coil ImageRequest  │
│  - size(width,height)│
│  - allowHardware()   │
│  - memoryCachePolicy │
│  - transformations   │
└──────────────────────┘
          │
          ▼
┌──────────────────────┐
│   Fetcher / Decoder  │
│  从磁盘取数据并解码 │
└──────────────────────┘
          │
          │  产生像素数据
          ▼
┌─────────────────────────────────────────────┐
│ Bitmap / Drawable                           │
│                                             │
│ 可能落在:                                   │
│ - Native Heap(软件Bitmap像素)             │
│ - Hardware Bitmap(底层图形相关内存)       │
│ - Java对象壳(Bitmap对象本身很小)          │
└─────────────────────────────────────────────┘
          │
          │ setImageDrawable / setImageBitmap
          ▼
┌─────────────────────────────────────────────┐
│ RecyclerView.ItemView / ImageView           │
│                                             │
│ - View 持有对 Drawable/Bitmap 的引用         │
│ - 当前可见item、预取item、复用item都会影响活跃图数量 │
└─────────────────────────────────────────────┘
          │
          │  UI线程 draw()
          ▼
┌─────────────────────────────────────────────┐
│ View 绘制命令录制                           │
│ - Canvas.drawBitmap() / drawDrawable()      │
│ - 不是直接"把像素写屏幕",而是记录绘制指令    │
└─────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────────────────────────┐
│ RenderThread / HWUI                         │
│                                             │
│ - 处理硬件加速渲染                           │
│ - 可能建立/复用纹理、图层、缓存               │
│ - bitmap参与GPU可用的图形资源                │
└─────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────────────────────────┐
│ Graphics 相关内存                           │
│                                             │
│ 可能包括:                                   │
│ - GPU纹理 / 纹理缓存                         │
│ - Hardware Bitmap backing store             │
│ - RenderNode / Layer / 离屏buffer           │
│ - gralloc / dmabuf / GraphicBuffer          │
│ - Window Surface buffer                     │
└─────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────────────────────────┐
│ SurfaceFlinger 合成显示                      │
│ - App窗口buffer交给系统合成到屏幕            │
└─────────────────────────────────────────────┘

Coil 负责把图片"准备成可显示的 Bitmap/Drawable",RecyclerView + ImageView 负责把它接入 View 树,RenderThread/HWUI 负责把它变成图形系统可渲染的内容,最终这些渲染相关资源中的一部分体现在 Graphics。

分四层理解:

  1. 图片数据层:文件、content uri

  2. 解码层:Coil 把它变成 Bitmap

  3. View 层:ImageView 引用并参与绘制

  4. 图形层:RenderThread / GPU / Surface buffer,最终表现为 Graphics

dumpsys meminfo 对照理解:

复制代码
[磁盘文件 jpg/png/webp]
        │
        │ 不算进进程运行时内存
        ▼
[Coil 解码]
        │
        ▼
┌──────────────────────────────┐
│ 1. Bitmap对象壳               │
│    -> 少量 Java Heap          │
└──────────────────────────────┘
        │
        ▼
┌──────────────────────────────┐
│ 2. Bitmap像素数据             │
│    -> 常见在 Native Heap      │
│    -> 若是 HARDWARE,更偏底层图形内存 │
└──────────────────────────────┘
        │
        │ 被 ImageView 显示
        ▼
┌──────────────────────────────┐
│ 3. 硬件加速绘制链路           │
│    -> RenderThread / HWUI     │
│    -> GPU纹理/缓存/图层        │
│    -> 这部分常表现为 Graphics  │
└──────────────────────────────┘
        │
        ▼
┌──────────────────────────────┐
│ 4. Window Surface Buffer      │
│    -> Graphics                │
│    -> 页面一显示就存在         │
└──────────────────────────────┘

这里最重要的是:

  • Bitmap像素内存 不等于 Graphics

  • Bitmap一旦显示 ,通常会牵动 Graphics

  • Graphics 里除了图片,还包括 窗口 buffer

因为列表页内存问题,关键不是某一张图,而是 同时活跃了多少张图

复制代码
RecyclerView 当前页面
│
├─ 屏幕可见 ViewHolder 12 个
│   └─ 12 张图正在显示
│
├─ 即将进入屏幕的预取 ViewHolder 6 个
│   └─ 6 张图可能已加载好
│
├─ Recycler / Cache 中暂存的 item 若干
│   └─ 可能仍持有旧 drawable 引用
│
├─ Coil Memory Cache
│   └─ 若干 bitmap 可快速复用/命中
│
└─ 正在 crossfade / transition 的 item
    └─ 旧图 + 新图同时参与绘制

所以 Graphics 上涨,往往是因为:

  • 单张图大

  • 同时活跃图多

  • 图形缓存和窗口缓冲叠加

一张更细的"单张图片流转图"

复制代码
[原始图片文件]
   4000 x 3000 JPEG, 3MB
          │
          │ Coil 解码
          ▼
[Bitmap]
   可能被解码成:
   300 x 300 x 4 = 351KB   (理想缩略图)
   或
   1200 x 1200 x 4 = 5.5MB (过大解码)
          │
          │ 设置给 ImageView
          ▼
[ImageView 持有 Drawable/Bitmap 引用]
          │
          │ RecyclerView 将 item 布局并绘制
          ▼
[UI线程记录 drawBitmap 指令]
          │
          ▼
[RenderThread / HWUI]
   - 将其纳入硬件加速渲染
   - 可能建立纹理/缓存/图层
          │
          ▼
[Graphics]
   - 纹理资源
   - 图形缓存
   - Surface合成相关buffer
          │
          ▼
[显示到屏幕]

同一张原图,真正决定 Graphics 压力的,不是磁盘大小 3MB,而是解码后的像素尺寸是否进入图形缓存路径

一个"Graphics 增长来源拆解图"

复制代码
打开一个 RecyclerView 图片列表页后,Graphics ≈

Graphics
├─ A. Window Surface Buffers
│    └─ 和屏幕分辨率、双/三缓冲有关
│
├─ B. 图片显示带来的图形资源
│    ├─ Bitmap被绘制后相关的纹理/缓存
│    ├─ Hardware Bitmap backing store
│    └─ RenderThread / HWUI 的缓存
│
├─ C. 特效额外开销
│    ├─ crossfade
│    ├─ alpha / shadow / rounded corners
│    ├─ 离屏渲染 layer
│    └─ 动画
│
└─ D. 滚动带来的短时峰值
     ├─ 预取图片
     ├─ 快速 bind / rebind
     ├─ 旧图新图交替
     └─ 暂时未释放的图形资源

文件不占 Graphics,解码尺寸决定底盘,View 显示触发图形链路,RenderThread/HWUI 把图片变成 Graphics。

复制代码
【RecyclerView + Coil 图片显示内存流转】

数据源(file/content/url)
        │
        ▼
Coil Request
(size / allowHardware / transformations / cache)
        │
        ▼
Decoder
(解码成 Bitmap/Drawable)
        │
        ├──────────────► Java Heap
        │                 (Bitmap对象壳、少量引用)
        │
        └──────────────► Native / Hardware Bitmap
                          (像素数据本体)
        │
        ▼
ImageView.setImage...
(View 持有图片引用)
        │
        ▼
UI线程绘制
(drawBitmap / drawDrawable)
        │
        ▼
RenderThread / HWUI
(硬件加速渲染、纹理/缓存/图层)
        │
        ▼
Graphics
├─ 图片相关图形资源
├─ 硬件位图相关内存
├─ 渲染缓存/离屏buffer
└─ Window Surface buffers
        │
        ▼
SurfaceFlinger
(系统合成后显示到屏幕)

附:Android HWUI是什么?

Android HWUI(Hardware Accelerated Rendering Engine for UI)是Android系统中用于处理UI渲染的硬件加速引擎。它的主要作用是利用GPU(图形处理单元)来加速UI的渲染过程,从而提高渲染效率和流畅度。以下是Android HWUI工作的主要方式和步骤:

一、基本工作原理

传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行。

HWUI基于GPU加速,通过Skia Backend与GPU进行交互,将UI绘制任务从CPU转移到GPU上执行。这种方式可以显著提高绘制性能,特别是在高分辨率屏幕和复杂UI场景下。

  • GPU相比CPU更加适合完成光栅化、动画变换等耗时任务,在移动设备上比起使用CPU来完成这些任务,GPU会更加省电,带来的用户体验也会更佳。
  • 现代移动GPU支持可编程管线,可以高效地开发应用界面的一些特效(吸入、渐变、模糊、阴影)

Skia Backend有两种:SkiaGl和SkiaVk,分别对应后端OpenGLES和Vulkan

二、核心组件

  1. Skia图形库:

    • Skia是HWUI的核心图形库,提供了基础的绘制功能,如图形、文本、位图等。

    • 它支持硬件加速渲染,能够充分利用GPU进行并发计算,加快UI界面的渲染速度。

    • 代码示例

      复制代码
      // 创建画布
      Canvas canvas = new Canvas(bitmap);
      // 绘制矩形
      Paint paint = new Paint();
      final int color = Color.BLUE;
      paint.setColor(color);
      RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
      canvas.drawRoundRect(rect, 32, 32, paint);
  2. OpenGL ES/Vulkan:

    • OpenGL ES/Vulkan是HWUI与GPU之间的桥梁,负责将Skia生成的绘制命令转化为GPU能够执行的指令序列。
    • OpenGL ES 3.0是HWUI的默认版本,具有更好的功能和性能表现。未来HWUI的Backend默认会切换到Vulkan。
  3. DisplayList:

    • DisplayList是HWUI的渲染列表,用于记录绘制操作以及它们的位置、大小等信息。

    • 这些操作被显式地缓存起来,以便后续可以更快速地进行处理,并允许视图树中相同的部分在多个帧之间重复使用,从而节约内存和带宽。

    • 代码示例

      复制代码
      // 创建DisplayList
      DisplayList displayList = new DisplayList("MyList");
      displayList.start(512, 512); //设置宽高
      // 添加绘制操作
      Paint paint = new Paint();
      paint.setColor(Color.RED);
      paint.setStyle(Paint.Style.FILL);
      displayList.drawRect(64, 64, 256, 256, paint);
      // 结束DisplayList
      displayList.end();
  4. RenderNode:

    • RenderNode是HWUI的渲染节点,对应于视图层次结构中的一个节点。

    • 每个View持有一个RenderNode,这些RenderNode组成树形结构,用于管理和组织渲染任务。

    • RenderNode负责保存和管理View绘制过程中使用的各种属性,如透明度和边框等。

    • 代码示例

      复制代码
      // 创建RenderNode
      RenderNode renderNode = new RenderNode("MyNode");
      // 更新RenderNode属性
      View view = findViewById(R.id.myView);
      renderNode.setPosition(view.getLeft(), view.getTop());
      renderNode.setElevation(view.getElevation());
      Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
      renderNode.setBitmapCache(bitmap);
      // 添加子节点
      RenderNode child = new RenderNode("MyChild");
      //更新child属性
      renderNode.addChild(child);
  5. Layers

    • Layer是HWUI的另一个重要概念,用于实现UI的专业效果和动画效果。

    • 在Android 10及以前的系统中,所有视图都隶属于共享的系统组层级。但是在Android 11及以后的系统中,每个窗口/活动/碎片都将拥有自己的独立图层级别,从而增强了性能和隐私管理。

    • 代码示例

      复制代码
      // 创建Layer
      Layer layer = mSurface.getLayer();
      layer.setX(100);
      layer.setY(100);
      layer.setAlpha(0.5f);
      // 在Layer上绘制图像和文字
      Canvas canvas = layer.lockCanvas();
      canvas.drawBitmap(bitmap, 0, 0, paint);
      canvas.drawText(text, 30, 100, paint);
      layer.unlockCanvasAndPost(canvas);
  6. HardwareComposer

    • HardwareComposer是Android系统中的硬件合成器,作为HWUI的主要接口之一。

    • 它的作用是在GPU输出图像到屏幕之前,对多个应用程序的渲染结果进行混合和组合,从而形成最终的显示内容。

    • 在硬件合成情况下,不需要将应用程序的UI渲染成一个帧缓存(FrameBuffer)并上传到系统RAM,从而减少内存和总线带宽的负担。

    • 代码示例

      复制代码
      RoutingTable routingTable = hwcomposer.createRoutingTable();
      routingTable.setOutputConfig(new OutputConfiguration(mDisplayToken, Display.DEFAULT_DISPLAY));
      Destination destination = routingTable.createDestination();
      destination.setColorMode(ColorMode.SRGB);
      destination.setBufferStream(bufferStream);
      surfaceFlinger.setTransactionState(routingTable.getTransaction());
      surfaceFlinger.applyTransaction();
  7. Threaded Rendering(多线程渲染):

    • 为了最大化利用CPU性能,HWUI采用了多线程渲染模式。
    • 通常包括UI线程和RenderThread(RT线程)。UI线程负责View的绘制逻辑和将绘制命令打包成Skia的绘制命令存储到DisplayList;RT线程则负责取出这些绘制命令并执行实际的渲染操作。

三、工作流程

  1. UI线程绘制:
    • 当View需要被绘制时,UI线程会调用相应的绘制方法(如onDraw())。
    • 这些绘制方法通过Canvas类提供的接口进行绘制操作,Canvas的绘制命令会被转化为Skia的绘制命令并存储到DisplayList中。
  2. RenderThread渲染:
    • RenderThread从DisplayList中取出绘制命令,并通过OpenGL ES接口将这些命令发送给GPU执行。
    • GPU完成绘制后,将渲染结果输出到屏幕上。
  3. 性能优化:
    • HWUI还采用了多种性能优化技术,如延时渲染列表(Deferred Display List)、绘制命令合并(Draw Op Batching)等。
    • 这些技术通过减少GPU的调用次数、优化渲染状态切换等方式来提高渲染效率。
相关推荐
idingzhi1 小时前
A股量化策略日报(2026年05月11日)
android·开发语言·python·kotlin
我命由我123451 小时前
Jetpack Compose - 设置 Compose 编译器、设置 Compose 依赖项
android·java·java-ee·kotlin·android jetpack·android-studio·android runtime
Kapaseker1 小时前
reified 如何骗过 JVM 类型擦除
android·kotlin
硬件学长森哥1 小时前
成像技术系列-3A算法基础
android·图像处理·计算机视觉
唔662 小时前
Android在局域网中搭建 MQTT服务器 协议V3.1.1
android·运维·服务器
2601_957418803 小时前
Android 手机如何通过 PTP / MTP 连接单反相机?源码级方案分享
android·数码相机·智能手机
阿巴斯甜11 小时前
ARouter
android
Andya_net13 小时前
MySQL | MySQL 8.0 权限管理实践-精确赋予库、表只读等权限
android·数据库·mysql
阿巴斯甜13 小时前
Map
android