轻智能预览器Previewer工作流程
·
一、整体架构概览
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 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) │ │ │
│ │ │ ↓ │ │ │
│ │ │ 发送到预览器前端 │ │ │
│ │ └───────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘

二、启动阶段
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 预览器的工作流程可以分为四个核心阶段:
- 启动初始化:创建虚拟屏幕和缓冲区
- 布局测量:计算视图位置和尺寸
- 渲染绘制:将视图绘制到 ARGB8888 缓冲区
- 缓冲区发送:转换为 RGB 格式并通过 WebSocket 发送
关键设计采用了策略模式,使得 RootView 不需要关心具体的显示设备,提高了代码的可扩展性和可维护性。
更多推荐
所有评论(0)