一、整体架构概览

┌─────────────────────────────────────────────────────────────────────────────────┐
│                           Previewer 预览器完整流程                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌──────────────────┐     ┌──────────────────┐     ┌──────────────────────┐   │
│  │  ThinPreviewer   │────▶│   JsAppImpl      │────▶│   GraphicStartUp     │   │
│  │   (入口启动)      │     │  (JS应用管理)     │     │   (图形系统初始化)    │   │
│  └──────────────────┘     └──────────────────┘     └──────────────────────┘   │
│                                                           │                  │
│                                                           ▼                  │
│  ┌──────────────────────────────────────────────────────────────────────┐     │
│  │                        VirtualScreenImpl                            │     │
│  │  ┌─────────────┐   ┌─────────────┐   ┌─────────────────────────┐    │     │
│  │  │ osBuffer    │   │screenBuffer │   │    GetFBBufferInfo()    │    │     │
│  │  │ (454x454   │   │ (RGB格式)   │   │  返回 bufferInfo       │    │     │
│  │  │  ARGB8888)  │   │ (发送用)     │   │  virAddr=osBuffer+40   │    │     │
│  │  └─────────────┘   └─────────────┘   └─────────────────────────┘    │     │
│  └──────────────────────────────────────────────────────────────────────┘     │
│                           │                              │                   │
│                           ▼                              ▼                   │
│  ┌──────────────────────────────────────────────────────────────────────┐     │
│  │                         RootView                                     │     │
│  │  ┌─────────────────────┐   ┌─────────────────────────────────────┐  │     │
│  │  │ UpdateBufferInfo()  │──▶│ dc_.bufferInfo = bufferInfo          │  │     │
│  │  │  (更新缓冲区信息)     │   │   virAddr = osBuffer + 40          │  │     │
│  │  └─────────────────────┘   └─────────────────────────────────────┘  │     │
│  │                           │                                         │     │
│  │                           ▼                                         │     │
│  │  ┌───────────────────────────────────────────────────────────────┐   │     │
│  │  │ Render() → RenderManager::RenderRect() → UIView::OnDraw()    │   │     │
│  │  │           (测量)      (渲染矩形)      (实际绘制到缓冲区)        │   │     │
│  │  └───────────────────────────────────────────────────────────────┘   │     │
│  │                           │                                         │     │
│  │                           ▼                                         │     │
│  │  ┌───────────────────────────────────────────────────────────────┐   │     │
│  │  │ BaseGfxEngine::GetInstance()->Flush(flushRect)               │   │     │
│  │  │           ↓                                                  │   │     │
│  │  │ VirtualScreenImpl::Flush()                                    │   │     │
│  │  │   - ARGB8888 → RGB 颜色转换                                   │   │     │
│  │  │   - osBuffer → screenBuffer                                   │   │     │
│  │  └───────────────────────────────────────────────────────────────┘   │     │
│  └──────────────────────────────────────────────────────────────────────┘     │
│                           │                                                │
│                           ▼                                                │
│  ┌──────────────────────────────────────────────────────────────────────┐     │
│  │                    ScheduleBufferSend()                              │     │
│  │  ┌───────────────────────────────────────────────────────────────┐   │     │
│  │  │ WebSocketServer::WriteData(screenBuffer, headSize + size)    │   │     │
│  │  │           ↓                                                  │   │     │
│  │  │           发送到预览器前端                                     │   │     │
│  │  └───────────────────────────────────────────────────────────────┘   │     │
│  └──────────────────────────────────────────────────────────────────────┘     │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────────┘

img


二、启动阶段

2.1 ThinPreviewer 入口

文件: ide/tools/previewer/ThinPreviewer.cpp

int main(int argc, char* argv[]) {
    // 1. 解析命令行参数
    CommandParser::GetInstance().ParseArgs(argc, argv);
    
    // 2. 初始化共享数据(亮度、电量等)
    InitSharedData();
    
    // 3. 初始化设备设置
    InitSettings();
    
    // 4. 初始化 JS 应用
    JsAppImpl::GetInstance().InitJsApp();
    
    // 5. 主循环
    while (!Interrupter::IsInterrupt()) {
        CommandLineInterface::GetInstance().ProcessCommand();
        manager.RunTimerTick();
    }
}

2.2 JsAppImpl 初始化

文件: ide/tools/previewer/jsapp/lite/JsAppImpl.cpp

void JsAppImpl::ThreadCallBack() {
    // 1. 图形系统初始化
    OHOS::GraphicStartUp::Init();
    
    // 2. 语言设置
    GLOBAL_ConfigLanguage(...);
    
    // 3. 输入设备初始化
    InitHalScreen();
    
    // 4. 字体引擎初始化
    InitFontEngine();
    
    // 5. 虚拟屏幕初始化(关键!)
    VirtualScreenImpl::GetInstance().InitAll(pipeName, pipePort);
    
    // 6. 启动 JS 应用
    StartJsApp();
}

三、缓冲区管理

3.1 VirtualScreenImpl 缓冲区创建

文件: ide/tools/previewer/mock/lite/VirtualScreenImpl.cpp

void VirtualScreenImpl::InitAll(std::string pipeName, std::string pipePort) {
    // 1. 设置图像解码能力
    OHOS::ImageDecodeAbility& ability = OHOS::ImageDecodeAbility::GetInstance();
    ability.SetImageDecodeAbility(OHOS::IMG_SUPPORT_BITMAP | ...);
    
    // 2. 初始化 WebSocket 管道
    InitPipe(pipeName, pipePort);
    
    // 3. 计算缓冲区大小
    // bufferSize = 宽度 × 高度 × 每像素字节数 + 头部大小
    bufferSize = orignalResolutionWidth * orignalResolutionHeight * pixelSize + headSize;
    
    // 4. 分配缓冲区
    wholeBuffer = new(std::nothrow) uint8_t[LWS_PRE + bufferSize];
    screenBuffer = wholeBuffer + LWS_PRE;           // JPEG 编码后的数据
    osBuffer = new(std::nothrow) uint8_t[bufferSize]; // ARGB8888 渲染目标
    
    // 5. 设置缓冲区信息
    bufferInfo->rect = {0, 0, compressionResolutionWidth - 1, compressionResolutionHeight - 1};
    bufferInfo->mode = OHOS::ARGB8888;
    bufferInfo->virAddr = osBuffer + headSize;  // 跳过头部 40 字节
}

缓冲区结构

缓冲区 用途 格式 大小
osBuffer 渲染目标 ARGB8888 454 × 454 × 4 + 40
screenBuffer 发送缓冲区 RGB 头部 + JPEG数据

关键常量

const size_t headSize = 40;   // 数据包头部长度(字节)
const uint16_t pixelSize = 4; // ARGB8888,4字节/像素
int jpgPix = 3;               // JPEG,3字节/像素(RGB)

3.2 RootView 获取缓冲区

文件: foundation/arkui/ui_lite/frameworks/components/root_view.cpp

void RootView::InitDrawContext() {
    // 获取虚拟屏幕的缓冲区信息
    dc_.bufferInfo = BaseGfxEngine::GetInstance()->GetFBBufferInfo();
    if (dc_.bufferInfo != nullptr) {
        InitMapBufferInfo(dc_.bufferInfo);
    }
}

四、渲染流程

4.1 RenderManager 回调(定时触发)

文件: foundation/arkui/ui_lite/frameworks/core/render_manager.cpp

void RenderManager::Callback() {
    // 非窗口模式下直接渲染 RootView
    RootView* rootView = RootView::GetInstance();
    rootView->Measure();   // 测量布局
    rootView->Render();    // 执行渲染
}

4.2 RootView 渲染

文件: foundation/arkui/ui_lite/frameworks/components/root_view.cpp

void RootView::Render() {
    // 1. 检查并设置 RootView 尺寸
    int16_t screenWidth = Screen::GetInstance().GetWidth();
    int16_t screenHeight = Screen::GetInstance().GetHeight();
    if (GetRect().GetWidth() != screenWidth || GetRect().GetHeight() != screenHeight) {
        SetPosition(0, 0);
        SetWidth(screenWidth);
        SetHeight(screenHeight);
    }
    
    // 2. 获取刷新区域
    Rect flushRect(GetScreenRect());
    
    // 3. 渲染矩形区域
    RenderManager::RenderRect(flushRect, this);
    
    // 4. 刷新缓冲区(触发 VirtualScreenImpl::Flush)
    BaseGfxEngine::GetInstance()->Flush(flushRect);
}

4.3 RenderManager::RenderRect

文件: foundation/arkui/ui_lite/frameworks/core/render_manager.cpp

void RenderManager::RenderRect(const Rect& rect, RootView* rootView) {
    Rect mask = rect;
    
    // 获取顶层视图并绘制
    UIView* topView = rootView->GetTopUIView(mask);
    rootView->DrawTop(topView, mask);
}

4.4 RootView::DrawTop(实际绘制)

文件: foundation/arkui/ui_lite/frameworks/components/root_view.cpp

void RootView::DrawTop(UIView* view, const Rect& rect) {
    // 1. 更新缓冲区信息(关键!)
    BufferInfo* gfxDstBuffer = dc_.bufferInfo;
    UpdateBufferInfo(gfxDstBuffer);
    
    // 2. 遍历视图树绘制
    while (par != nullptr) {
        if (curView != nullptr && curView->IsVisible()) {
            // 裁剪视图区域
            bool intersectResult = curViewRect.Intersect(maskedRect, mask);
            
            if (intersectResult) {
                // 绘制视图(实际写入 dc_.bufferInfo->virAddr)
                curView->OnDraw(*dc_.bufferInfo, curViewRect);
            }
        }
    }
}

五、缓冲区刷新与发送

5.1 VirtualScreenImpl::Flush(颜色格式转换)

文件: ide/tools/previewer/mock/lite/VirtualScreenImpl.cpp

void VirtualScreenImpl::Flush(const OHOS::Rect& flushRect) {
    // 1. 颜色格式转换:ARGB8888 → RGB
    for (int i = 0; i <= compressionResolutionHeight - 1; ++i) {
        for (int j = 0; j <= compressionResolutionWidth - 1; ++j) {
            uint8_t* curPixel = screenBuffer + (i * compressionResolutionWidth + j) * jpgPix + headSize;
            uint8_t* osPixel = osBuffer + (i * compressionResolutionWidth + j) * pixelSize + headSize;
            
            // ARGB8888 → RGB(跳过 Alpha 通道)
            *(curPixel + redPos) = *(osPixel + 1);   // R
            *(curPixel + greenPos) = *(osPixel + 2); // G
            *(curPixel + bluePos) = *(osPixel + 3);  // B
        }
    }
    
    // 2. 标记缓冲区已变化
    isChanged = true;
    
    // 3. 调度发送
    ScheduleBufferSend();
}

5.2 ScheduleBufferSend(WebSocket 发送)

文件: ide/tools/previewer/mock/lite/VirtualScreenImpl.cpp

void VirtualScreenImpl::ScheduleBufferSend() {
    if (!isChanged || !isWebSocketConfiged) {
        return;
    }
    
    // 发送完整缓冲区
    SendFullBuffer();
    isChanged = false;
}

六、完整调用链总结

阶段 文件 函数 主要职责
启动 ThinPreviewer.cpp main() 入口启动
初始化 JsAppImpl.cpp ThreadCallBack() JS应用初始化
缓冲区创建 VirtualScreenImpl.cpp InitAll() 创建 osBuffer/screenBuffer
图形系统 graphic_startup.cpp Init() 初始化渲染管理器等
渲染触发 render_manager.cpp Callback() 定时触发渲染
测量 root_view.cpp Measure() 测量布局
渲染 root_view.cpp Render() 执行渲染
绘制 root_view.cpp DrawTop() 绘制视图树
缓冲区刷新 VirtualScreenImpl.cpp Flush() 颜色转换
发送 VirtualScreenImpl.cpp ScheduleBufferSend() WebSocket发送

七、数据流总结

┌─────────────────────────────────────────────────────────────────────────┐
│                         数据流路径                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                       │
│  UIView::OnDraw()                                                      │
│       │                                                                │
│       ▼                                                                │
│  dc_.bufferInfo->virAddr  ──────────────────────────────────────┐     │
│       │                                                          │     │
│       │  指向                                                     │     │
│       ▼                                                          │     │
│  osBuffer + headSize (ARGB8888格式)                              │     │
│       │                                                          │     │
│       │  VirtualScreenImpl::Flush()                              │     │
│       ▼                                                          │     │
│  screenBuffer + headSize (RGB格式)                               │     │
│       │                                                          │     │
│       │  ScheduleBufferSend()                                    │     │
│       ▼                                                          │     │
│  WebSocketServer::WriteData()                                    │     │
│       │                                                          │     │
│       ▼                                                          │     │
│  预览器前端                                                        │     │
│                                                                       │
└─────────────────────────────────────────────────────────────────────────┘

八、关键设计模式

8.1 策略模式(Strategy Pattern)

// 抽象接口
class BaseGfxEngine {
public:
    virtual void Flush(const Rect& rect) = 0;
    static void InitGfxEngine(BaseGfxEngine* engine);
    static BaseGfxEngine* GetInstance();
};

// 具体实现(预览器专用)
class VirtualScreenImpl : public BaseGfxEngine {
public:
    void Flush(const Rect& rect) override;
};

// 使用方式
void RootView::Render() {
    // 不关心具体实现,只调用抽象接口
    BaseGfxEngine::GetInstance()->Flush(flushRect);
}

优点

  • RootView 不需要知道具体的显示设备类型
  • 可以轻松切换不同的显示后端(窗口、虚拟屏幕、硬件加速等)

九、常见问题排查

9.1 预览器黑屏问题

排查步骤 检查项 日志关键字
1 缓冲区是否分配成功 Memory allocation failed
2 图形引擎是否注册 BaseGfxEngine registered successfully
3 RootView 尺寸是否正确 RootView rect: (0, 0, 453, 453)
4 视图是否可见 IsVisible: 1
5 视图区域是否与屏幕相交 intersect: 1
6 缓冲区是否有内容 nonZeroSamplePixels > 0
7 WebSocket 是否连接 Websocket client connect

9.2 颜色显示异常

原因:ARGB8888 → RGB 转换错误

// 错误代码
*(curPixel + redPos) = *(osPixel + bluePos);   // 错误:读取了 Alpha 通道
*(curPixel + greenPos) = *(osPixel + greenPos); // 错误:读取了 Red 通道
*(curPixel + bluePos) = *(osPixel + redPos);    // 错误:读取了 Green 通道

// 正确代码
*(curPixel + redPos) = *(osPixel + 1);   // 正确:读取 Red 通道
*(curPixel + greenPos) = *(osPixel + 2); // 正确:读取 Green 通道
*(curPixel + bluePos) = *(osPixel + 3);  // 正确:读取 Blue 通道

十、总结

Previewer 预览器的工作流程可以分为四个核心阶段:

  1. 启动初始化:创建虚拟屏幕和缓冲区
  2. 布局测量:计算视图位置和尺寸
  3. 渲染绘制:将视图绘制到 ARGB8888 缓冲区
  4. 缓冲区发送:转换为 RGB 格式并通过 WebSocket 发送

关键设计采用了策略模式,使得 RootView 不需要关心具体的显示设备,提高了代码的可扩展性和可维护性。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐