Skip to content

JSView 小程序本地化 ​

JSViewApp 自 1.3.413.20230831 起支持小程序本地化运行。
无网时也可用本地缓存 / APK 内预制包启动小程序。

说明(近期重构)
对外接口名与行为保持稳定,内部存储已切换为 jsv_localization_v2(以 index.json 为真相源,按 seq 管理版本,默认最多保留 3 个版本)。
旧目录 cacheDir/localizationJs/ 不再使用,升级后会按新路径重新落盘。

推荐依赖:

groovy
implementation("com.tvcode.js_view_app:JSViewApp:1.4.792.20260908") // 使用含本地化重构的版本

业务侧一般通过 SyncJSLocalizationUtil 调用(主进程直连,子进程会绑定 JSLocalizationService)。


1. 本地打包一份小程序代码到 APK ​

在项目根目录 build.gradle 添加插件依赖:

groovy
buildscript {
    dependencies {
        classpath "com.android.tools.build:gradle:4.1.3"
        // 本地化打包插件
        classpath 'com.qcode.jslocalizationplugin:JSLocalizationPlugin:1.1.1'
    }
}

在 app module 的 build.gradle 启用插件:

groovy
plugins {
    id 'com.android.application'
    id 'com.qcode.jslocalizationplugin'
}

生成代码包:

shell
# JS_URL:小程序入口 js 地址;APP_NAME:小程序 appName
./gradlew :app:jsLocalization \
  -PJS_URL=http://localhost:8077/js/main.jsv.5209aedd.mjs \
  -PAPP_NAME=shijiuTv.com/TestDemoCompile

产物目录:

app/build/localizationJsAsset/localizationJs/

将其中的 zip(或整个 localizationJs 目录)拷到:

app/src/main/assets/localizationJs/

命名约定:{appName 中的 / 换成 .}.zip
例如 app.qcast.cn/chipadventure → app.qcast.cn.chipadventure.zip。

必须随 APK 内置对应 Core ​

预制包的 app.cfg 里有 COREVERSIONRANGE。
从 asset 首次解压安装时,要求该 Core 已在设备上可用(通常随 APK 打进 embedded aar);缺失会安装失败,不会写入半截索引。

查看所需 core 版本:用实际环境的 jsv-info.json / 包内 app.cfg。
aar 找相关人员获取,例如:

groovy
repositories {
    flatDir { dirs 'libs' }
}
dependencies {
    // 预制引擎(版本号与 COREVERSIONRANGE 对应)
    implementation(name: "jsview.core-embedded-xxxxx.full", ext: "aar")
}

若宿主 minSdk 低于 aar 声明值,需在 AndroidManifest 的 tools:overrideLibrary 中增加 com.qcode.jsview.CoreEmbedded(仅编译合并,运行时仍须确认 API 兼容)。


2. 运行时获取本地化启动参数 ​

java
SyncJSLocalizationUtil.INSTANCE.getLastLocalizationParams(
        getApplicationContext(),
        "shijiuTv.com/TestDemoCompile",
        new ILocalizationCallback.Stub() {
            @Override
            public void onResult(MiniAppParams miniAppParams) {
                // 使用本地化参数启动
                mMainPageProxy.loadParams(miniAppParams);
            }

            @Override
            public void onJSDownloaded(MiniAppParams miniAppParams) {
                // getLast 路径不会回调
            }

            @Override
            public void onJSNothingToUpdate() {
                // getLast 路径不会回调
            }

            @Override
            public void onException(int errorCode, String msg) {
                // ERROR_CODE_CACHE_EMPTY:磁盘无可用包,且 asset 也未成功安装
                // ERROR_CODE_UNKNOWN:其它失败(含 asset 所需 Core 未随 APK 内置)
            }
        });

getLastLocalizationParams 逻辑 ​

mermaid
flowchart TD
  A[getLastLocalizationParams] --> B[读 index.json 列出磁盘完整包]
  B --> C{有候选?}
  C -->|是| D[current 优先,其余按 seq 降序]
  D --> E[逐个确保 Core 可用]
  E -->|成功| F[设为 current 并返回 MiniAppParams]
  E -->|失败| G[尝试下一版本]
  G --> E
  C -->|否| H[尝试 assets 预制 zip]
  H -->|Core 已内置| I[解压 commit 后返回]
  H -->|Core 缺失 / 无 zip| J[onException CACHE_EMPTY / UNKNOWN]

要点:

  • 磁盘包需同时满足:READY + app.cfg + dist/ 且 dist 结构合法。
  • 已是 current 的命中不会无故增加 seq;只有切换到其它版本(promote)才会刷新 seq。
  • 磁盘都不可用时才解压 asset;asset 安装要求 Core 已内置,不会联网补 Core。
  • 有网时,对索引里的旧包可尝试补下缺失 Core 后再用。

3. 更新本地化小程序 ​

空闲时可主动更新:

java
private void doCheckUpdate() {
    MiniAppParams miniAppParams = new MiniAppParams();
    miniAppParams.setUrl("http://192.168.0.27:8077/js/main.jsv.5209aedd.mjs");
    // 可选;不设则从同级 jsv-info.json 解析 AppName
    // miniAppParams.setAppName("shijiuTv.com/TestDemoCompile");

    SyncJSLocalizationUtil.INSTANCE.doDownloadJS(
            getApplicationContext(),
            miniAppParams,
            new ILocalizationCallback.Stub() {
                @Override
                public void onResult(MiniAppParams miniAppParams) {
                    // doDownload 路径不会回调
                }

                @Override
                public void onJSDownloaded(MiniAppParams miniAppParams) {
                    // 已下载/安装或切换到其它已有版本
                }

                @Override
                public void onJSNothingToUpdate() {
                    // 目标版本已是 current 且完整,无需更新(不 bump seq)
                }

                @Override
                public void onException(int errorCode, String msg) {
                    // ERROR_CODE_FAIL_APP_NAME:appName 与远端 jsv-info 不一致
                    // ERROR_CODE_NETWORK_DISCONNECT:无网
                    // ERROR_CODE_UNKNOWN:其它
                }
            });
}

doDownloadJS 逻辑 ​

mermaid
flowchart TD
  A[doDownloadJS] --> B{有网?}
  B -->|否| C[onException NETWORK_DISCONNECT]
  B -->|是| D[拉 jsv-info / 校验 AppName]
  D --> E{本地已有该版本且完整?}
  E -->|是且已是 current| F[onJSNothingToUpdate]
  E -->|是但非 current| G[切换为 current + bump seq → onJSDownloaded]
  E -->|否| H[并行: 下 Core + 下 engine/dist.dat]
  H --> I[staging 校验后 commit]
  I --> J[淘汰超出 maxVersions 的旧包]
  J --> K[onJSDownloaded]

要点:

  • 安装走 staging → 校验 → commit,失败清理 staging,避免半截包。
  • 默认每个 app 最多保留 3 个版本(按 seq 淘汰非 current)。
  • Core 下载可与包下载并行;安装失败会取消等待,避免堵死本地化线程。

4. 磁盘布局(实现细节) ​

每个小程序一份目录:

text
filesDir/jsv_localization_v2/{safeAppName}/
  index.json          # currentVersion / nextSeq / entries
  staging/            # 安装临时区
  packages/{version}/
    READY
    app.cfg
    engine.js?        # 可选
    dist/

index.json 中每个版本记录 core、bytes、seq、ready 等;版本新旧以单调递增的 seq 为准,不依赖文件时间戳。


5. 其它 API ​

java
// 清空某 app 的本地化缓存(v2 目录整棵删除)
SyncJSLocalizationUtil.INSTANCE.clearLocalVersion(context, appName);

6. 错误码 ​

常量含义
ERROR_CODE_CACHE_EMPTY本地无可用包,asset 也未装上
ERROR_CODE_FAIL_APP_NAME传入 appName 与远端包不一致
ERROR_CODE_NETWORK_DISCONNECT更新时无网络
ERROR_CODE_UNKNOWN其它失败(含 asset Core 未内置等)