移动端跨平台¶
Warning
The current page still doesn't have a translation for this language.
You can read it through google translate.
SOUI 的移动端覆盖 Android / iOS / 鸿蒙(HarmonyOS / OHOS),业务侧仍是同一份 C++ 代码。但三者在 swinx 中的实现方式并不相同——iOS 与桌面端同类,Android 与鸿蒙才是真正特殊的那一组。
平台分层的整体说明见 跨平台开发概述。
关键分野:谁拥有启动器¶
| iOS | Android / 鸿蒙 | |
|---|---|---|
| 进程入口 | 应用自己的 main() |
系统的 Java Activity / ArkTS UIAbility |
| 谁创建应用对象 | swinx 内部调 UIApplicationMain |
系统运行时(JVM / ArkTS 引擎) |
| 谁拥有窗口 / 事件循环 | swinx | 系统运行时 |
| 平台层目录 | swinx/src/platform/ios |
swinx/src/platform/mobile |
| 平台 API 实现在哪 | swinx 内部 | 外部桥接库注册进 g_platformAPI |
是否用 platform_api |
否 | 是 |
iOS 虽然是移动端,但它的应用进程仍然可以拥有自己的 main(),swinx 只要在里面调用 UIApplicationMain 就能完整掌控窗口与事件循环。所以 iOS 与 Linux / macOS 属于同一类:平台 API 全部在 swinx 内部实现。
Android 与鸿蒙则不同:进程由系统运行时拉起,窗口(View / ArkUI 组件)、事件循环(Looper / ArkTS 事件循环)、输入法、剪贴板全部归系统运行时所有,C++ 侧无法自建。因此这两个平台必须由桥接库把能力注册进 swinx 的函数指针表:
soui-android-lib/src/main/cpp/src/AndroidPlatformAPIReg.cpp→PlatformAPI_Init(...)soui-ohos-lib/src/main/cpp/src/OhosPlatformAPIReg.cpp→PlatformAPI_Init(...)
平台支持矩阵¶
| 平台 | 桥接技术 | 桥接库 | swinx 平台层 | swinx 2D 后端 | SOUI 渲染工厂 | 入口 |
|---|---|---|---|---|---|---|
| Android | JNI(静态注册) | soui-android-lib |
src/platform/mobile |
Cairo | Render_Skia |
android_entry.cc(JNI 函数内) |
| iOS | 无(Objective-C++ 直编) | 无 | src/platform/ios |
Core Graphics | Render_Skia |
main() → swinx_ios_entry(argc, argv, _tWinMain) |
| 鸿蒙 OHOS | N-API | soui-ohos-lib |
src/platform/mobile |
Cairo | Render_Skia |
ohos_entry.cc(N-API 函数内) |
不要把两层渲染混为一谈
Android 与鸿蒙的 swinx 2D 后端是 Cairo(mobile.cmake 编译 src/gdi/cairo),Skia 是 SOUI 上层的渲染工厂(cfg.SetRender(Render_Skia))。旧版文档把这一栏写成"Skia 离屏渲染",混淆了这两个不同层次。
HWND 载体¶
三端的 HWND 数值都指向"一个原生窗口对象",但查找方式有区别:
| 平台 | HWND 的实际含义 | C++ → 原生对象 | 原生侧 → 原生对象 |
|---|---|---|---|
| iOS | SUIView(UIView 子类)指针 |
(__bridge UIView *)(void *)hWnd,直接解引用 |
同为一个对象,无需查表 |
| Android | Java 窗口对象 jobject GlobalRef 的地址( reinterpret_cast<jlong>(m_javaRef),见 SouiSurfaceProxy.h) |
(jobject)hWnd,无需查表 |
Java 侧用 SouiPlatformBridge.mViewMap(HashMap<Long, View>)映射回 View |
| 鸿蒙 OHOS | 由 C++ surf_nativeCreate 生成的 native 句柄,经 ArkTS nativeGetHwnd() 返回 |
直接作为 swinx 内部句柄使用 | ArkTS 侧用 SouiPlatformBridge.mWindowMap(Map<number, INativeWindow>)映射,入口是 hwndAsNativeWindow(hwnd) |
也就是说:iOS 是真正的零查表;Android 在 C++ 侧零查表、Java 侧仍需映射表;鸿蒙两侧都通过映射表。
共享的核心机制¶
无论哪个移动端平台,都遵循下面几条设计——这才是业务代码能零改动移植的基础:
- 窗口系统仿真:还原 Win32 窗口模型(HWND、消息队列、
SetTimer、SetCapture、焦点与捕获),SOUI 核心无需修改。 - 消息语义对齐:触摸 / 按键事件统一转换为
WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP、WM_KEYDOWN等 Win32 消息,业务侧消息处理逻辑跨端一致。 - String Slot 字符串槽:C++ 与系统运行时之间传递字符串(如
WM_SETTEXT/WM_GETTEXT、IME 文本)时使用统一的字符串槽机制,规避跨语言编码与生命周期问题。Android 与鸿蒙的 slot 实现语义完全一致。 - screenId 激活栈:多窗口 / 多 Ability 场景下,用
screenId与激活栈把新建窗口路由到正确的屏幕容器(createWindow的第二个参数即screenId)。 - 虚拟 HWND:
mobile.cmake打开了-DENABLE_VIRTUAL_HWND,允许把系统运行时侧的原生控件(如 AndroidEditText、鸿蒙NativeEditView)注册为参与 SOUI 窗口体系的"虚拟 HWND"(RegisterVirtualHWND/UnregisterVirtualHWND,见swinx/include/wnd.h)。这是 Android 与鸿蒙独有的能力。 - 平台化资源加载:
SAppCfg为每个平台提供对应的资源来源接口(见下表)。
三端差异速览¶
| 方面 | Android | iOS | 鸿蒙 OHOS |
|---|---|---|---|
| 桥接技术 | JNI(静态注册,库名固定 soui4android) |
无,Objective-C++ 直接混编 | N-API |
| 消息循环驱动 | Java Handler 调度 C++ 排空 |
CFRunLoopRunInMode |
ArkTS setTimeout(0)(scheduleMessageProcessing) |
| 定时器 | Handler.postDelayed |
swinx 内部 SConnection::SetTimer,由 CFRunLoop 超时驱动 |
ArkTS setTimeout / clearTimeout;跨线程时用 isJsThread() 判定 + napi_threadsafe_function |
| 上屏方式 | AndroidBitmap_lockPixels 写像素 → Canvas.drawBitmap |
SUIView 的 drawRect: 中用 CGContext 绘制 |
缓冲区同步到 image.PixelMap → ctx.drawImage(走 GPU 纹理,比 putImageData 快很多) |
| 脏矩形 | Java 侧维护 | 系统更新区(setNeedsDisplayInRect:) |
C++ 回传 rcUpdate,ArkTS 按 px 解释 drawImage 的 dx/dy/dw/dh |
| 跨线程约束 | 需 JavaVM::AttachCurrentThread |
UIKit 调用必须在主线程 | napi_env 仅在 JS 线程有效 |
| 资源接口 | SetSysResAndroidAsset / SetAppResAndroidAsset |
Bundle 内目录(SetSysResFile / SetAppResFile) |
SetSysResOhosRawFile / SetAppResOhosRawFile |
入口对比¶
iOS 与桌面端共用 _tWinMain,只是多了一层 swinx_ios_entry 包装(games/cnchess/client/main.cc):
#if defined(__IOS__)
int main(int argc, char **argv)
{
return swinx_ios_entry(argc, argv, _tWinMain); // swinx 内部调 UIApplicationMain
}
#endif
Android 与鸿蒙的入口必须落在 JNI / N-API 函数里,因此单独提供 android_entry.cc / ohos_entry.cc。但里面初始化 SOUI 的写法与 _tWinMain 完全一致,只是资源来源不同:
// games/cnchess/client/android_entry.cc
cfg.SetRender(Render_Skia)
...
.SetSysResAndroidAsset(assetMgr, _T("soui_sys_res"))
.SetAppResAndroidAsset(assetMgr, _T("uires"));
// games/cnchess/client/ohos_entry.cc
cfg.SetRender(Render_Skia);
cfg.SetSysResOhosRawFile(resMgr, _T("soui_sys_res"));
cfg.SetAppResOhosRawFile(resMgr, _T("uires"));
同一份业务代码
games/cnchess/client(中国象棋)的 main.cc 一份覆盖 Windows / Linux / macOS / iOS;Android 与鸿蒙只是换了入口文件。业务窗体、控件、布局、消息处理在六个平台上完全一致,不同的只有"应用如何启动 SOUI"这一小段。
各平台适配指南¶
- Android 适配指南:JNI 静态注册、
soui-android-lib、CMake 片段复用、Asset 资源 - iOS 适配指南:
swinx_ios_entry、SUIView、CFRunLoop 消息泵、Bundle 资源 - 鸿蒙(OHOS)适配指南:N-API 桥接、
SouiPlatformBridge、PixelMap 上屏、跨线程定时器 - 移动端专项优化:用
SModalView替代SHostDialog、SPanel滚动条 fling 与OnLButtonDownEx/OnLButtonUpEx迁移