ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

茶器艺科智造HarmonyOS应用实战-47-onPageEnd就把ArkWeb标成ready,Three.js异常谁来接住:建立loading、ready、error状态机

茶器艺科智造HarmonyOS应用实战-47-onPageEnd就把ArkWeb标成ready,Three.js异常谁来接住:建立loading、ready、error状态机 茶器艺科智造HarmonyOS应用实战-47-onPageEnd就把ArkWeb标成readyThree.js异常谁来接住建立loading、ready、error状态机onPageEnd到达时ArkWeb 主页面完成了一次加载它并不等于茶杯三维引擎已经创建 Scene、WebGLRenderer、OrbitControls、Mesh并且暴露了window.__cupSync。当前CupWeb3d.ets:489-492却在这个回调里直接执行webReady true随后推参数、补 STL、刷贴图、重置视角。原生侧的 ready 实际表达的是页面事件而业务需要的是 Three.js 能力握手。rawfilecup3d/index.html的顶层 IIFE 先访问THREE、创建渲染器和控制器直到文件末尾才定义window.__cupSync。如果脚本缺失、WebGLRenderer 创建失败或前置初始化抛异常后续函数可能根本没有注册原生脚本又使用window.__cupSync ...函数不存在时只是短路不会进入 catch。于是可能出现“原生认为 ready、画面空白、参数调用看似无错”的失败组合。本文把网页加载、引擎握手、失败和重试建成显式状态机。内容来自静态源码与官方 API 语义建议代码未写入只读项目也未运行 ArkWeb、Three.js 或真机恢复流程。一、当前webReady只有false和true来源过于宽泛组件只声明一个布尔值// 当前源码摘录 State private webReady: boolean false; .onPageEnd(() { this.webReady true; void this.onWebPageReadySequence(); })参数、STL、贴图与视角重置都以它为门禁。onWebPageReadySequence的顺序设计得很清楚但前提没有被证明它只知道主页面回调到了不知道__cupSync、__cupEmitStlToNative、贴图函数和 Three 渲染循环是否可用。当前 Web 组件没有附加onPageBegin、onErrorReceive或onRenderExited状态处理JavaScriptProxy 的 methodList 只有stlBegin、stlAppend、stlFinish、cupTextureReport没有引擎 ready/error 方法。源文件里也没有原生可观察的启动握手。二、Three初始化异常可能发生在__cupSync定义之前HTML 先加载本地three.min.js与OrbitControls.js随后立即运行 IIFE。前部代码直接使用全局变量// 当前初始化次序摘要(function(){varcontainerdocument.getElementById(c);varscenenewTHREE.Scene();varcameranewTHREE.PerspectiveCamera(45,1,0.1,2000);varrenderernewTHREE.WebGLRenderer({antialias:true,alpha:true});varcontrolsnewTHREE.OrbitControls(camera,renderer.domElement);// 中间定义轮廓、贴图、STL、动画等大量逻辑tick();window.__cupSyncfunction(p){/* ... */};})();只要THREE未定义、container 异常、WebGL 上下文不可用或某个早期语句抛错执行会在函数发布前中断。源码没有包住整个 bootstrap 的顶层 try/catch也没有window.onerror或unhandledrejection向 ArkTS 上报。__cupSync自己的 catch 只写console.error([cup3d], e)原生没有据此切换状态。三、onPageEnd应只推进加载阶段不直接宣布ready华为官方 ArkWeb 说明中onPageEnd是主 frame 的页面加载完成回调官方 FAQ 还说明收到它不能保证下一帧已经反映 DOM 状态。对本地 HTML 来说它适合发起能力探测不适合作为 Three 业务能力成功的替代证据。建议状态至少包含四种// 建议代码 export enum CupEngineState { IDLE IDLE, LOADING LOADING, READY READY, ERROR ERROR } export interface CupEngineFailure { readonly stage: RESOURCE | BOOT | HANDSHAKE | RENDER | PROCESS; readonly code: string; readonly detail: string; readonly retryable: boolean; }状态转换应固定为IDLE - LOADING开始加载LOADING - READY收到匹配本轮 token 的 JS 握手LOADING/READY - ERROR资源、引擎或进程错误ERROR - LOADING用户重试。onPageEnd仍保持 LOADING只发握手请求并启动超时。四、用带loadToken的握手证明函数已经发布仅让 JS 页面启动时主动报 ready 仍可能遇到旧页面回调。建议每次加载生成loadTokenonPageEnd调用已经初始化完成后才会存在的window.__cupHandshake(token)Web 再把同一 token 回传。旧 token 一律忽略。// 建议代码放在成功完成 Three 初始化并发布业务函数之后window.__cupHandshakefunction(loadToken){varhosttypeofCupNative!undefined?CupNative:window.CupNative;if(!host||!host.cupEngineReady){returnNO_NATIVE_BRIDGE;}host.cupEngineReady(String(loadToken||),three-r128,JSON.stringify({sync:!!window.__cupSync,stl:!!window.__cupEmitStlToNative}));returnREADY_REPORTED;};握手应位于__cupSync、STL 和必要贴图入口定义之后。capabilities 让原生按能力放行不要仅靠版本字符串猜函数存在。token 不需要是秘密只用于区分加载代次。五、JavaScriptProxy扩展ready与error回调现有CupNativeStlBridge已证明 Web 可以回调 ArkTS。建议将引擎事件拆成小桥或在现有对象中增加两个经过长度限制的方法。回调只传稳定 stage/code 与截断详情不把完整堆栈直接展示给用户。// 建议代码桥接层不直接操作 WebviewController class CupEngineBridge { private onReady: (token: string, version: string, caps: string) void (): void {}; private onError: (token: string, stage: string, code: string, detail: string) void (): void {}; attach( ready: (token: string, version: string, caps: string) void, error: (token: string, stage: string, code: string, detail: string) void ): void { this.onReady ready; this.onError error; } cupEngineReady (token: string, version: string, caps: string): void { this.onReady(token, version, caps); }; cupEngineError ( token: string, stage: string, code: string, detail: string ): void { this.onError(token, stage, code, detail.substring(0, 240)); }; }注册时把cupEngineReady和cupEngineError加入 JavaScriptProxy methodList。若另建CupEngine代理对象要用当前 SDK 实际支持的注册方式本文示例不声称已经编译。六、Native侧把页面事件、握手和超时接到状态机开始加载时清理旧超时与错误生成新 tokenonPageEnd 只注入握手脚本。超时阈值应通过低端设备启动数据确定下面的 5000ms 只是示例配置。// 建议代码省略安全随机token实现细节 State private engineState: CupEngineState CupEngineState.IDLE; private loadToken: string ; private handshakeTimer: number -1; private onWebLoadBegin(): void { this.cancelHandshakeTimer(); this.loadToken this.nextLoadToken(); this.engineState CupEngineState.LOADING; this.engineFailure undefined; } private onWebPageEnd(): void { const tokenJson JSON.stringify(this.loadToken); void this.webController.runJavaScript( window.__cupHandshake window.__cupHandshake(${tokenJson}); ); this.armHandshakeTimeout(5000); } private acceptEngineReady(token: string, capsJson: string): void { if (token ! this.loadToken || !this.hasRequiredCapabilities(capsJson)) { return; } this.cancelHandshakeTimer(); this.engineState CupEngineState.READY; this.scheduleParamsToWeb(); this.flushDeferredActions(); }若脚本函数不存在runJavaScript本身可能正常完成但不会收到 ready超时负责把这种静默短路变成 HANDSHAKE_ERROR。参数调度应沿用第 46 篇的 pending latest snapshot而不是在超时前丢弃用户最新修改。七、Web端顶层异常与运行期异常都要回传应在危险初始化之前安装最小错误上报函数并用顶层 try/catch 包住 bootstrap。不能等 Three 初始化完成后才定义错误桥因为最需要接住的是启动早期异常。// 建议代码错误码需固定枚举detail只用于诊断(functionbootCupEngine(){functionreportFatal(stage,code,error){try{varhosttypeofCupNative!undefined?CupNative:window.CupNative;vardetailString(errorerror.message?error.message:error||);if(hosthost.cupEngineError){host.cupEngineError(window.__cupLoadToken||,stage,code,detail.slice(0,240));}}catch(_){}}try{if(!window.THREE)thrownewError(THREE_UNAVAILABLE);initializeSceneRendererControls();publishCupCommands();}catch(error){reportFatal(BOOT,ENGINE_BOOT_FAILED,error);}window.addEventListener(unhandledrejection,function(event){reportFatal(RENDER,UNHANDLED_REJECTION,event.reason);});})();上例中的 token 需要由握手请求先写入或让 error 回调不依赖 token、由当前 LOADING 代次接收。两种协议只能选一种并写清顺序。WebGL context lost、同步重建失败和动画循环异常也应调用相同上报入口普通 console 日志可保留但不能作为唯一状态通道。八、所有命令只在READY消费失败时保留可恢复意图参数、贴图、STL、重置视角的门禁应读取engineState READY。LOADING 时保留最新参数和未消费 nonceERROR 时停止自动执行让用户看到错误状态并决定重试。只有真正提交命令后才更新last...Handled否则重试后无法补发。// 建议代码 private canRunCupCommand(): boolean { return this.engineState CupEngineState.READY; } private flushDeferredActions(): void { if (!this.canRunCupCommand()) { return; } this.scheduleParamsToWeb(); this.kickCupTextureFlushIfNeeded(); this.tryFlushCupViewReset(); this.tryFlushStlExport(); } private retryEngine(): void { if (this.engineState ! CupEngineState.ERROR) { return; } this.onWebLoadBegin(); this.webController.loadUrl($rawfile(cup3d/index.html)); }loadUrl对 rawfile Resource 的具体重载形式要按当前 SDK 签名核对也可以通过组件 key 重建 Web。重试必须有次数或用户动作门禁避免渲染进程持续崩溃时形成自动循环。九、验证矩阵覆盖加载、启动、运行和恢复场景注入条件期望状态延迟动作正常启动函数全部发布LOADING→READY只补发最新参数three脚本不可用THREE缺失LOADING→ERROR不发 STL/贴图renderer构造抛错可控 fakeBOOT ERROR显示重试入口握手函数缺失onPageEnd 到达超时后 ERRORpending 保留旧 token 回调重载后旧消息状态不变不消费新 pending资源加载错误onErrorReceiveRESOURCE ERROR记录稳定错误码渲染进程退出onRenderExitedPROCESS ERROR需要显式 reloadsync重建异常非法 profileRENDER ERROR不伪装参数成功加载中改参数连续拖动仍为 LOADINGREADY 后发最终快照ERROR后重试用户点击ERROR→LOADING→READYnonce 不重复消费单元测试可用 fake bridge 和 fake timer断言转换HTML 可在测试页面中让initializeSceneRendererControls主动抛错最终还需真实 ArkWeb 与 WebGL 设备测试。仅看到 onPageEnd 日志不能作为 READY 证据。十、故障排查、官方锚点与结论范围一直停在 LOADING 时先运行一个只返回typeof window.__cupHandshake的脚本区分函数未发布、JavaScriptProxy 不可见和 token 不匹配不要直接把超时延长。进入 READY 仍空白时检查 capability 是否只验证了函数存在却没验证首帧再加入 renderer、scene、cupMesh 的最小状态回报。立即进入 ERROR 时按 stage 分路RESOURCE 看本地脚本路径和 onErrorReceiveBOOT 看 THREE、OrbitControls 与 WebGLRendererPROCESS 看 onRenderExitedRENDER 看最后 revision 与杯型参数。重试后收到旧回调则核对 loadToken 生命周期和桥对象是否残留上轮 listener。本次源码确认onPageEnd直接置webReadytrue业务函数发布晚于多步 Three 初始化参数脚本对函数不存在采用短路现有 proxy 没有引擎状态方法。官方资料确认 onPageEnd 是主 frame 加载完成回调资源错误与渲染进程退出另有事件。本文没有复现实际 Three 异常也没有证明建议 API 在项目目标 SDK 编译通过状态机、握手、超时和 reload 都属于待实现方案。
返回列表