From b225d8cc1f2b8b86e47a42847b441b0d21d599a4 Mon Sep 17 00:00:00 2001 From: sunnylqm Date: Thu, 5 Sep 2024 13:58:53 +0000 Subject: [PATCH] deploy: 1e104d502a51fff8195012378b9bdcc9a8b23aa3 --- 404.html | 2 +- .../_buildManifest.js | 2 +- .../_ssgManifest.js | 0 _next/static/chunks/nextra-data-en-US.json | 2 +- .../docs/getting-started-71e1575098153bf8.js | 1 - .../docs/getting-started-bb565f6f3bf98c17.js | 1 + docs/api.html | 4 ++-- docs/bestpractice.html | 4 ++-- docs/changelog.html | 4 ++-- docs/cli.html | 4 ++-- docs/faq.html | 4 ++-- docs/getting-started.html | 18 +++++++++++++++--- docs/integration.html | 4 ++-- docs/publish.html | 4 ++-- index.html | 2 +- pricing.html | 2 +- 16 files changed, 35 insertions(+), 23 deletions(-) rename _next/static/{ckMop5w30pR8Wl0LCO95h => IqkBIxjQI6IuFq9_STIsl}/_buildManifest.js (95%) rename _next/static/{ckMop5w30pR8Wl0LCO95h => IqkBIxjQI6IuFq9_STIsl}/_ssgManifest.js (100%) delete mode 100644 _next/static/chunks/pages/docs/getting-started-71e1575098153bf8.js create mode 100644 _next/static/chunks/pages/docs/getting-started-bb565f6f3bf98c17.js diff --git a/404.html b/404.html index 1eb8056..13d5a00 100644 --- a/404.html +++ b/404.html @@ -1 +1 @@ -404: This page could not be found

404

This page could not be found.

\ No newline at end of file +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/_next/static/ckMop5w30pR8Wl0LCO95h/_buildManifest.js b/_next/static/IqkBIxjQI6IuFq9_STIsl/_buildManifest.js similarity index 95% rename from _next/static/ckMop5w30pR8Wl0LCO95h/_buildManifest.js rename to _next/static/IqkBIxjQI6IuFq9_STIsl/_buildManifest.js index 8a3645d..e04d218 100644 --- a/_next/static/ckMop5w30pR8Wl0LCO95h/_buildManifest.js +++ b/_next/static/IqkBIxjQI6IuFq9_STIsl/_buildManifest.js @@ -1 +1 @@ -self.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,"static/chunks/513-aa5d85e05f9d4121.js","static/chunks/pages/index-2cb378b650e8a471.js"],"/_error":["static/chunks/pages/_error-4f469fabddeb9740.js"],"/docs/api":[s,"static/chunks/pages/docs/api-0e73153339c8fc93.js"],"/docs/bestpractice":[s,"static/chunks/pages/docs/bestpractice-ead96873b3acec42.js"],"/docs/changelog":[s,"static/chunks/pages/docs/changelog-10f87d7355f7c23b.js"],"/docs/cli":[s,"static/chunks/pages/docs/cli-41b24fff9d34f2ab.js"],"/docs/faq":[s,"static/chunks/pages/docs/faq-140da79f2b356e74.js"],"/docs/getting-started":[s,"static/chunks/pages/docs/getting-started-71e1575098153bf8.js"],"/docs/integration":[s,"static/chunks/pages/docs/integration-7eb79b0e6960a674.js"],"/docs/publish":["static/chunks/42a66924-7f61374ecffda3ae.js",s,"static/chunks/521-a924734bdb9d2826.js","static/chunks/pages/docs/publish-9b87dd49988cf135.js"],"/pricing":[s,"static/chunks/598-51a9d588edd87ff7.js","static/chunks/pages/pricing-63fa684436b87b54.js"],sortedPages:["/","/_app","/_error","/docs/api","/docs/bestpractice","/docs/changelog","/docs/cli","/docs/faq","/docs/getting-started","/docs/integration","/docs/publish","/pricing"]}}("static/chunks/781-e03c9a788935ae97.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file +self.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,"static/chunks/513-aa5d85e05f9d4121.js","static/chunks/pages/index-2cb378b650e8a471.js"],"/_error":["static/chunks/pages/_error-4f469fabddeb9740.js"],"/docs/api":[s,"static/chunks/pages/docs/api-0e73153339c8fc93.js"],"/docs/bestpractice":[s,"static/chunks/pages/docs/bestpractice-ead96873b3acec42.js"],"/docs/changelog":[s,"static/chunks/pages/docs/changelog-10f87d7355f7c23b.js"],"/docs/cli":[s,"static/chunks/pages/docs/cli-41b24fff9d34f2ab.js"],"/docs/faq":[s,"static/chunks/pages/docs/faq-140da79f2b356e74.js"],"/docs/getting-started":[s,"static/chunks/pages/docs/getting-started-bb565f6f3bf98c17.js"],"/docs/integration":[s,"static/chunks/pages/docs/integration-7eb79b0e6960a674.js"],"/docs/publish":["static/chunks/42a66924-7f61374ecffda3ae.js",s,"static/chunks/521-a924734bdb9d2826.js","static/chunks/pages/docs/publish-9b87dd49988cf135.js"],"/pricing":[s,"static/chunks/598-51a9d588edd87ff7.js","static/chunks/pages/pricing-63fa684436b87b54.js"],sortedPages:["/","/_app","/_error","/docs/api","/docs/bestpractice","/docs/changelog","/docs/cli","/docs/faq","/docs/getting-started","/docs/integration","/docs/publish","/pricing"]}}("static/chunks/781-e03c9a788935ae97.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/ckMop5w30pR8Wl0LCO95h/_ssgManifest.js b/_next/static/IqkBIxjQI6IuFq9_STIsl/_ssgManifest.js similarity index 100% rename from _next/static/ckMop5w30pR8Wl0LCO95h/_ssgManifest.js rename to _next/static/IqkBIxjQI6IuFq9_STIsl/_ssgManifest.js diff --git a/_next/static/chunks/nextra-data-en-US.json b/_next/static/chunks/nextra-data-en-US.json index c86697b..e4faf64 100644 --- a/_next/static/chunks/nextra-data-en-US.json +++ b/_next/static/chunks/nextra-data-en-US.json @@ -1 +1 @@ -{"/docs/api":{"title":"API参考","data":{"javascript-方法#JavaScript 方法":"","new-pushyoptions-pushyoptions#new Pushy(options: PushyOptions)":"创建 Pushy 热更新服务实例,其构造参数如下:\ninterface PushyOptions {\n // 必填,通过pushy createApp或selectApp命令,或在网页管理端获取\n appKey: string;\n // 如已购买私有部署服务,可在此自定义私有服务器地址\n server?: {\n // 主节点\n main: string;\n // 备用节点群\n backups?: string[];\n // 远程查询节点接口\n queryUrl?: string;\n };\n // 自定义日志输出,也可用于上报统计数据\n logger?: ({ type, data }: { type: EventType; data: EventData }) => void;\n // 触发自动检查更新的策略\n checkStrategy?:\n | \"onAppStart\" // 仅在app启动时\n | \"onAppResume\" // 仅在app从后台切换到前台时\n | \"both\"; // 默认值,同时包含前两个场景\n | null; // 不自动检查更新,必须手动调用checkUpdate方法,此选项需 v10.4.2+ 版本\n // 自动下载和应用更新的策略\n updateStrategy?:\n | \"alwaysAlert\" // 调试环境(__DEV__)默认值,使用系统默认的alert页面提示热更且会在有报错时弹出提示\n | \"alertUpdateAndIgnoreError\" // 生产环境默认值,在有热更时使用系统默认的alert页面提示热更,但不弹出任何报错提示\n | \"silentAndNow\" // 自动静默下载并立刻应用热更\n | \"silentAndLater\"; // 自动静默下载,但仅在用户退出app后重启时应用更新\n | null; // 不自动下载和应用更新,如需自定义热更界面请选择此项\n // 是否在热更重启后自动标记为成功,默认为true\n autoMarkSuccess?: boolean;\n // 是否在若干ms后自动清除最后的报错,默认为不清除\n dismissErrorAfter?: number;\n // 是否在开发环境中检查热更,默认为false。如需在开发环境中调试热更,请打开此选项。\n // 但即便打开此选项,也仅能检查、下载热更,并不能实际应用热更。实际应用热更必须在release包中进行。\n // 此选项需 v10.4.2+ 版本\n debug?: boolean;\n // 是否在调用 checkUpdate 和 downloadUpdate 时抛出错误,默认为不抛出错误,通过 lastError 获取错误信息\n // 启用后可以使用 try catch 语句 捕获错误,同时 lastError 也仍然可用\n // try {\n // await checkUpdate();\n // } catch (e) {\n // console.error(e);\n // }\n // 此选项需 v10.8.0+ 版本\n throwError?: boolean;\n // 在检查更新前执行,返回 false 则取消检查更新\n // 此选项需 v10.12.0+ 版本\n beforeCheckUpdate?: () => Promise;\n // 在下载更新前执行,返回 false 则取消下载更新,可以配合自定义的 metaInfo 做一些条件控制\n // 此选项需 v10.12.0+ 版本\n beforeDownloadUpdate?: (info: UpdateInfo) => Promise;\n}\n// 日志事件类型\ntype EventType =\n // 更新失败,重启后发生回滚\n | \"rollback\"\n // 检查更新时报错\n | \"errorChecking\"\n // 正在发起检查\n | \"checking\"\n // 正在下载更新\n | \"downloading\"\n // 更新失败\n | \"errorUpdate\"\n // 更新成功\n | \"markSuccess\"\n // 下载apk\n | \"downloadingApk\"\n // 下载apk前申请存储权限被用户拒绝\n | \"rejectStoragePermission\"\n // 下载apk前申请存储权限发生错误\n | \"errorStoragePermission\"\n // 下载apk时发生错误\n | \"errorDownloadAndInstallApk\";\n// 日志事件数据\ninterface EventData {\n // 当前已完成的热更hash值,如尚未热更则为空字符串\n currentVersion: string;\n // 客户端版本信息\n cInfo: {\n pushy: string; // 当前pushy版本\n rn: string; // 当前rn版本\n os: string; // 当前操作系统及版本\n uuid: string; // 用户标识符\n };\n // 客户端原生版本号\n packageVersion: string;\n // 编译时间戳\n buildTime: number;\n // 报错相关的信息\n message?: string;\n // 发生回滚的版本hash值\n rolledBackVersion?: string;\n // 更新失败的新版本hash值\n newVersion?: string;\n // 其他一些数据\n [key: string]: any;\n}","usepushy#usePushy()":"热更相关的工具函数。\n注意,在使用 的当前组件(一般是根组件)中无法直接调用usePushy,只有当前组件的子组件才能调用。\nconst {\n checkUpdate,\n switchVersion,\n switchVersionLater,\n markSuccess,\n dismissError,\n downloadUpdate,\n downloadAndInstallApk,\n getCurrentVersionInfo,\n parseTestQrCode,\n currentHash,\n packageVersion,\n client,\n progress,\n updateInfo,\n lastError,\n} = usePushy();\n其类型定义和功能如下:\ninterface PushyContext {\n // 检查更新\n checkUpdate: () => Promise;\n // 下载热更完成后调用,立即重启切换新版本\n switchVersion: () => void;\n // 下载热更完成后调用,用户手动重启app后切换新版本(静默更新)\n switchVersionLater: () => void;\n // 热更完成重启后,手动标记热更完成\n markSuccess: () => void;\n // 清除最后的报错状态\n dismissError: () => void;\n // 下载热更\n downloadUpdate: () => Promise;\n // 下载并安装apk\n downloadAndInstallApk: (url: string) => Promise;\n // 获取当前已热更版本的信息\n getCurrentVersionInfo: () => Promise<{\n name?: string;\n description?: string;\n metaInfo?: string;\n }>;\n // 解析测试二维码,此方法需 v10.11.2+ 版本\n parseTestQrCode: (qrCode: string) => void;\n // 当前的版本hash\n currentHash: string;\n // 当前的原生版本号\n packageVersion: string;\n // 当前的pushy热更服务示例\n client?: Pushy;\n // 下载开始后的进度数据\n progress?: {\n hash: string;\n // 已下载的字节数\n received: number;\n // 待下载的总字节数\n total: number;\n };\n // 热更相关信息\n updateInfo?: {\n // 已是最新版本,无需热更\n upToDate?: true;\n // 当前原生版本已过期,需要下载新的原生版本\n expired?: true;\n // 在pushy网页管理端设置的原生版本下载地址\n downloadUrl?: string;\n // 是否存在新的热更\n update?: true;\n // 新热更的版本名称\n name?: string;\n // 新热更的hash值\n hash?: string;\n // 新热更的更新说明\n description?: string;\n // 新热更携带的额外元数据\n metaInfo?: string;\n // 当前热更是否已暂停\n paused?:\n | \"app\" // 当前应用所有原生版本暂停\n | \"package\"; // 仅当前原生版本暂停\n // 其他信息\n message?: string;\n };\n // 检查、下载、应用热更等过程中的最新一次报错\n lastError?: Error;\n}","async-function-checkupdate#async function checkUpdate()":"触发更新检查,更新usePushy中的updateInfo(注意checkUpdate方法本身没有返回值),返回值有三种情形:\n{expired: true}:该应用原生包已过期(三种情况:1. 主动设置为过期状态,2. 主动删除,3. 从未上传),需要引导用户下载或跳转到应用市场(需要在网页管理端设置中填写downloadUrl)。如需在应用内执行 apk 更新,还需配置安装权限。\n{\n expired: true,\n downloadUrl: 'http://appstore/downloadUrl',\n}\n{upToDate: true}:当前已经更新到最新,无需进行更新。\n{update: true}:当前有新版本可以更新。name、description字段可以用于展示给用户版本号,更新内容等信息,而metaInfo字段则可以根据你的需求自定义一些标记(如是否静默更新、是否强制更新等等,自己根据标记的属性做一些条件流程控制),具体用法可参考场景实践。另外还有几个字段,包含了热更新文件的下载地址,\n{\n update: true,\n name: '1.0.3-rc',\n hash: 'hash',\n description: '添加聊天功能\\n修复商城页面BUG',\n metaInfo: '{\"silent\":true}',\n pdiffUrl: 'http://update-packages.reactnative.cn/hash',\n diffUrl: 'http://update-packages.reactnative.cn/hash',\n}","async-function-downloadupdate#async function downloadUpdate()":"下载热更包。仅当update:true时实际进行下载。会更新progress数据。","async-function-downloadandinstallapkurl#async function downloadAndInstallApk(url)":"下载更新的 apk 包并直接安装。url必须为可直接下载到 apk 文件的地址。注意要使用这个功能还需要在AndroidManifest.xml中手动添加安装权限,如果需要考虑 Android 7.0 以下的客户,则还需要添加外部存储权限。\n\n\n\n注意某些应用市场可能会因为上述权限拒绝应用上架。去掉上述两个权限并不影响热更新功能。","function-marksuccess#function markSuccess()":"手动调用此函数作为更新成功的标记(否则下次启动会默认失败自动回滚)。默认情况下不需调用此函数,除非设置autoMarkSuccess为false。","async-function-getcurrentversioninfo#async function getCurrentVersionInfo()":"获取当前已热更版本的信息(如尚未热更过则返回空对象)。返回值示例:\n{\n name: '1.0.3-rc',\n description: '添加聊天功能\\n修复商城页面BUG',\n metaInfo: '{\"silent\":true}',\n}","function-switchversion#function switchVersion()":"立即重启应用,并加载已经下载完毕的版本。","function-switchversionlater#function switchVersionLater()":"在下一次启动应用的时候加载已经下载完毕的版本。","function-parsetestqrcodeqrcode-string#function parseTestQrCode(qrCode: string)":"解析测试二维码,一般用于给 QA 人员测试热更新。如果在应用中已有扫码功能,则可以在应用中扫描 pushy 后台的测试二维码来测试任意版本的热更包。注意使用此方法,上述界面中的\"使用 Deep Link\"选项 请不要 勾选。代码示例:\n {\n // 识别到二维码后先关闭相机\n setShowCamera(false);\n // 先解析是否是pushy的测试二维码\n if (parseTestQrCode(codeStringValue)) {\n // 如果是pushy的测试二维码,则不再做其他业务扫码逻辑\n return;\n }\n // 如果不是,继续处理其他业务扫码逻辑\n }}\n/>","android-方法#Android 方法":"","updatecontextsetcustominstancemanagerreactinstancemanager-instancemanager#UpdateContext.setCustomInstanceManager(ReactInstanceManager instanceManager)":"如果是集成/混编 Android 方案,则可以使用此方法传入你自行创建的 ReactInstanceManager。自v5.5.8版本起可用。示例:\nimport cn.reactnative.modules.update.UpdateContext\nmReactInstanceManager = ReactInstanceManager.builder()\n // ...各种setter,但注意不要调用setBundleAssetName\n .setJSBundleFile(UpdateContext.getBundleUrl(mContext, \"assets://index.android.bundle\"))\n .build();\nUpdateContext.setCustomInstanceManager(mReactInstanceManager);"}},"/docs/bestpractice":{"title":"场景实践","data":{"优化原生和热更包体积#优化原生和热更包体积":"","ios-原生包优化ipa#iOS 原生包优化(ipa)":"对于同一份 archive(其版本号、编译时间和内置 bundle 已固定,不会受导出方式所影响),可以用不同选项多次导出 ipa,选择其中最小的上传到 pushy 服务器作为热更基准包。","android-原生包优化apk#Android 原生包优化(apk)":"apk 的优化主要考虑两个方向:\n启用 proguard 压缩混淆源码。但这一步可能导致一些使用反射的代码运行时报错,启用后需要充分测试每个页面和功能,以及需要阅读一些第三方关于 proguard 的特别设置说明。\n分开编译不同的 cpu 架构。找到android/app/build.gradle中的 cpu 架构部分,如下所示启用enable选项:\nsplits {\n abi {\n reset()\n- enable enableSeparateBuildPerCPUArchitecture\n+ enable true // 启用单独的 cpu 架构编译\n universalApk false // If true, also generate a universal APK\n }\n}\n如此一来会在编译目录中输出多个 apk 文件,分发和上传到热更新服务时只需要使用app-arm64-v8a-release.apk文件,可以大幅减小 apk 的大小。","热更新包优化ppk#热更新包优化(ppk)":"热更新包的主要内容是 js 包和其所引用的静态资源(主要是图片)。\njs 包成分分析。可以借助一些第三方工具(如react-native-bundle-visualizer)来分析 js 文件中哪些占比较大,是否可以用其他库替换等(如 dayjs 替换 moment,lodash-es 替换 lodash)。\n图片优化。\n在保证体验的情况下,使用一些工具对图片进行裁剪压缩。\n如不需要图片的透明像素,可以考虑将 png 格式转为 jpg 格式。\n还可以进一步考虑压缩比更高的图片格式,例如 webp 格式(需要第三方原生插件例如react-native-webp-format),heif 格式(iOS 11 和 Android 10 以上原生支持)等。这里有个图片格式对比可供参考 https://compare.rokka.io/_compare/#heif=40&jpeg=80&webp=80&av1=40&width=800。","有很多渠道包需要热更如何操作比较方便#有很多渠道包需要热更,如何操作比较方便?":"如果渠道包的js代码和初始资源有差别(无论多么细微的差别都会生成不同的 jsbundle),那么只能单独生成 apk,分别上传和绑定。可以考虑写一些脚本自动调用 cli 来执行批量操作。\n如果渠道包的js代码和初始资源完全一致,可以考虑使用Flavor 构建,或其他一些动态生成渠道包的方案(比如腾讯的 VasDolly,美团的 walle等),这样所有的渠道包基于同一个基础 apk 生成(因而会有相同的编译时间戳和 jsbundle)。这样可以只用上传一个基础 apk,对此 apk 的热更操作可以对所有渠道包生效。\n如果您是高级版、专业版或大客户VIP版客户,也可以考虑在管理后台的应用设置中启用忽略编译时间戳。此选项仅对比版本号,不校验时间戳,可提供更宽容的热更策略,但可能消耗更多流量。","如何支持-aab-格式的原生包#如何支持 aab 格式的原生包?":"如果您需要使用 aab 格式的 android 原生包,那么可以在上传到 Google play 之后,在其控制台中下载转换后的 apk 格式(见下图),然后将这个 apk 包上传到热更新的后台,即可正常支持热更新。","ci-的集成#CI 的集成":"在开发环境中,每次 bundle 都会生成一个不同名字的 ppk 文件,这不利于持续集成(CI)系统的引入。要解决这个问题,你可以使用--output参数来指定输出 ppk 文件的名字和路径,便于进行自动发布。","测试发布与回滚#测试、发布与回滚":"自 v10.11.2 版本开始,可以使用以下两种快捷扫码方案来测试热更,而无需提前进行绑定:\n若应用启用了 DeepLink 功能\n代码中无需任何改动,只需在上述界面中勾选“使用 Deep Link”,填入您应用的协议名,例如\"pushy://\",然后使用系统相机或系统内置的扫一扫功能扫码(注意不能使用微信扫码),即可自动调起应用并触发更新。\n若应用自带扫码功能\n请参考 parseTestQrCode 方法的说明。先发布一个测试包,再发布一个除了版本号以外均完全相同的正式包。例如,假设我们有一个正式包,版本为1.6.0,那么可以修改版本号重新打包一个1001.6.0,以一个明显不太正常的版本号来标识它是一个测试版本,同时后几位相同,可以表明它和某个正式版本存在关联(内容/依赖一致)。在每次往发布包发起热更新之前,先对测试包1001.6.0进行更新操作,基本测试通过之后,再在网页后台上将热更包重新绑定到正式包1.6.0上。如果在测试包中发现了重大问题,你就可以先进行修复,更新测试确认通过后再部署到正式线上环境。这样,可以最大程度的避免发生线上事故。万一确实发生线上事故需要回滚的话,先立即对原生包或者整个应用设置暂停热更,然后更改绑定到之前正常的版本,或者利用版本控制系统回滚代码到正常的状态,然后重新生成热更包并推送。","元信息meta-info的使用#元信息(Meta Info)的使用":"在发布热更新版本时,或者在网页端,你可以编辑版本的元信息。这是一段在检查更新时可以获得的字符串,你可以在其中按你所想的格式(一般建议用JSON 格式)保存一些信息。比如我们可以在元信息中约定字段标志silent,表示需要静默更新。当我们上传热更包填写 metainfo 时,以JSON 格式输入:\n{ \"silent\": true }\n请注意,我们并不对输入做任何格式校验和约束,请自行校验输入是否正确。\n此时在客户端检查更新时,能获取到我们刚刚输入的元信息,但它并不具备任何功能,只是一个字符串而已。所以我们其实需要预先在更新流程中加入对应的处理逻辑:\n// 调用 checkUpdate 获取 updateInfo\nif (updateInfo.expired) {\n // ... 原生包版本过期,下载或跳转下载页面\n} else if (updateInfo.upToDate) {\n // ... 没有更新,弹提示或忽略\n} else {\n // 有更新,一般来说我们在这里给用户弹窗提示,让用户选择是否更新\n // 那么静默更新的本质其实就是不弹窗,直接执行,所以可以在这里加入额外的判断流程\n // ...\n}\n我们在原有的更新流程中加入元信息的读取和判断:\nlet metaInfo = {};\ntry {\n // 注意 JSON 输入有可能有错误,需要用 try 语句来避免应用被带崩\n metaInfo = JSON.parse(updateInfo.metaInfo);\n} catch (e) {\n // 异常处理,忽略或上报?\n}\nif (metaInfo.silent) {\n // 如果热更包携带有 silent 字段,不询问用户,直接执行更新\n switchVersion();\n} else {\n // 否则还是走之前的询问流程\n // Alert.alert('提示', '检查到新的版本.......\n}\n又比如,可能某个版本包含一些重要的公告内容,所以还可以在上面插入一个公告字段等等。如何使用元信息,完全取决于您的想象力!"}},"/docs/changelog":{"title":"更新日志","data":{"10542024-04-23#10.5.4(2024-04-23)":"修复 web 端的兼容问题","10422024-04-22#10.4.2(2024-04-22)":"加入 debug 参数,允许在开发环境中调试热更\n热更策略中加入 null 选项,即禁止自动检查,完全手动控制","10102024-02-24#10.1.0(2024-02-24)":"全新设计的 api 接口,全面 hook 化,更易集成和自定义\n全面同时支持新老架构和 hermes","9002023-09-02#9.0.0(2023-09-02)":"初步支持新架构\n添加事件回调以方便统计","8012022-07-05#8.0.1(2022-07-05)":"没有 update.json 文件的情况下不会再报错","7412022-05-04#7.4.1(2022-05-04)":"修复极少数热更失败的情况","7342021-11-04#7.3.4(2021-11-04)":"新增快速集成方法。\n默认使用 AndroidX 支持库(使用npx jetify -r命令转换可以支持老的 support 库)","6402021-10-06#6.4.0(2021-10-06)":"新增查看本地版本信息的方法(getCurrentVersionInfo())","6302021-10-04#6.3.0(2021-10-04)":"库文件体积优化","6202021-08-13#6.2.0(2021-08-13)":"修复 android 7 及以下版本可能解压失败的问题","6102021-07-29#6.1.0(2021-07-29)":"修复频繁调用下载更新导致的图片丢失问题","6022021-05-18#6.0.2(2021-05-18)":"修复 android 7 及以下版本安装 apk 报错的问题","6002021-05-04#6.0.0(2021-05-04)":"换用 hdiff 算法,更新所需流量更少,速度更快\n修复一些少见的崩溃问题","51002020-12-18#5.10.0(2020-12-18)":"提升 iOS 更新的稳定性","5902020-09-27#5.9.0(2020-09-27)":"可在应用内直接下载安装新版本 apk","5832020-09-24#5.8.3(2020-09-24)":"加入下载进度回调","5702020-08-13#5.7.0(2020-08-13)":"初始化时检查 Android 的 bundle url 是否正确配置","5602020-05-26#5.6.0(2020-05-26)":"修复 iOS 更新偶尔报找不到 app.json 的问题","559-2020-04-14#5.5.9 (2020-04-14)":"修复编译时找不到 generateiOSBuildTime.sh 的问题","558-2020-04-02#5.5.8 (2020-04-02)":"提供 setCustomInstanceManager 方法,方便自己集成 RN 的用户调用","556-2020-02-11#5.5.6 (2020-02-11)":"bundle 时清除缓存\n修复更新包过大时可能出现的崩溃\npublish 时检查文件格式","555-2020-01-18#5.5.5 (2020-01-18)":"提升服务健壮性","554-2020-01-13#5.5.4 (2020-01-13)":"防止某些情况下安卓找不到 bundle 文件引起的崩溃","553-2019-12-18#5.5.3 (2019-12-18)":"忽略 hermes 的输出避免 buffer 溢出","552-2019-12-06#5.5.2 (2019-12-06)":"修复使用 use_frameworks 时无法读取时间戳的问题","550-2019-11-24#5.5.0 (2019-11-24)":"打包时加入时间戳","540-2019-11-16#5.4.0 (2019-11-16)":"修改类名方法名","532-2019-10-25#5.3.2 (2019-10-25)":"可以禁用 ios 端以避免审核被拒","530-2019-09-19#5.3.0 (2019-09-19)":"替换 apk reader 以避免某些环境读取 apk 版本号报错的问题","529-2019-09-18#5.2.9 (2019-09-18)":"添加 proguard 混淆规则以解决开启混淆后闪退的问题","528#5.2.8":"解决某些情形下 Android 调用 switchVersion 不能重启的问题","527#5.2.7":"改进 windows 端打更新包的兼容性(部分 windows 机器上会产生空 ppk 文件)","524#5.2.4":"支持 RN 0.61 的 hermes(路径变化)\niOS 端使用第三方的 SSZipArchive 以减少重名冲突","522#5.2.2":"修复一处导致 iOS 回滚的问题","521#5.2.1":"检测如果开启了 hermes,则自动编译为 hermes 字节码格式","520#5.2.0":"添加 typescript 声明\n支持 cocoapods","519#5.1.9":"重写 bundle 命令以提升版本兼容性\n改进命令行的输出样式","518#5.1.8":"服务器迁移到 https\nandroid 支持 64 位","516#5.1.6":"解决 Android 热更新后部分图片丢失问题:\n同一个项目中放置了多个完全相同的文件,在 5.1.0 至 5.1.5 之间的版本中,更新后有时会出现其中的部分无法显示。此问题在 5.1.6 版本修复。修复此问题涉及原生部分,需要重新打包。"}},"/docs/cli":{"title":"命令行工具","data":{"安装#安装":"$ npm install -g react-native-update-cli","命令#命令":"","pushy-bundle#pushy bundle":"生成资源包\nplatform: ios|android 对应的平台\nentryFile: 入口脚本文件\nintermediaDir: 临时文件输出目录\noutput: 最终 ppk 文件输出路径\ndev: 是否打包开发版本\nsourcemap: 是否生成 sourcemap(需 cli 版本 1.11.0+)","pushy-parseipa-ipafile#pushy parseIpa [ipaFile]":"解析 ipa 文件并输出一些相关信息,如版本号,编译时间戳等。","pushy-parseapk-apkfile#pushy parseApk [apkFile]":"解析 apk 文件并输出一些相关信息,如版本号,编译时间戳等。","pushy-diff-originnext#pushy diff [origin][next]":"提供两个 ppk 文件,生成从 origin 到 next 版本的差异更新包。\noutput: diff 文件输出路径","pushy-difffromapk-apkfilenext#pushy diffFromApk [apkFile][next]":"提供一个 apk 文件和一个 ppk 文件,生成从 apk 文件到 next 版本的差异更新包。如果使用热更新开放平台,你不需要自己执行此命令。\noutput: diff 文件输出路径","pushy-difffromipa-ipafilenext#pushy diffFromIpa [ipaFile][next]":"提供一个 ipa 文件和一个 ppk 文件,生成从 ipa 文件到 next 版本的差异更新包。如果使用热更新开放平台,你不需要自己执行此命令。\noutput: diff 文件输出路径","pushy-login-emailpwd#pushy login [email][pwd]":"登录热更新开放平台。你需要先登录才能使用下面的命令。","pushy-logout#pushy logout":"登出并清除本地的登录信息","pushy-me#pushy me":"查看自己是否已经登录,以及昵称等信息。","pushy-createapp#pushy createApp":"创建应用并立刻绑定到当前工程。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台\nname: 应用名称\ndownloadUrl: 应用安装包的下载地址","pushy-deleteapp-appid#pushy deleteApp [appId]":"删除已有应用。所有已创建的应用包、热更新版本都会被同时删除。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-apps#pushy apps":"查看当前已创建的全部应用。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-selectapp-appid#pushy selectApp [appId]":"绑定应用到当前工程。\nplatform: ios|android 对应的平台","pushy-uploadipa-ipafile#pushy uploadIpa [ipaFile]":"上传 ipa 文件到开放平台。\nnote: 备注(cli 需 1.24.0 或更高版本)","pushy-uploadapk-apkfile#pushy uploadApk [apkFile]":"上传 apk 文件到开放平台。\nnote: 备注(cli 需 1.24.0 或更高版本)","pushy-packages#pushy packages":"查看已经上传的原生包。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-publish-ppkfile#pushy publish [ppkFile]":"发布新的热更新版本(ppk 文件)。\nplatform: ios|android 对应的平台\nname: 当前热更新版本的名字(版本号)\ndescription: 当前热更新版本的描述信息,可以对用户进行展示\nmetaInfo: 当前热更新版本的元信息,可以用来保存一些额外信息,具体用法可参考场景实践。","pushy-versions#pushy versions":"分页列举可用的版本。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-update#pushy update":"为一个原生包版本绑定一个热更新版本。这项操作也可以在网页管理端进行。以下参数中packageId,packageVersion,minPackageVersion和maxPackageVersion四选一即可。\nplatform: ios|android 对应的平台\nversionId: 要绑定的热更新版本 ID\npackageId: 要绑定的原生包 ID (四选一)\npackageVersion: 要绑定的原生包版本名(四选一,需 cli 版本 1.7.2+)\nminPackageVersion: 要绑定的最低原生包版本,大于等于此版本的将逐个绑定(四选一,需 cli 版本 1.27.0+)\nmaxPackageVersion: 要绑定的最高原生包版本,小于等于此版本的将逐个绑定(四选一,需 cli 版本 1.27.0+)"}},"/docs/faq":{"title":"常见问题","data":{"":"如果本页面没能回答您的疑问,您可以去issues 区或 QQ 群 729013783 提问,或给我们发邮件。","业务问题#业务问题":"","热更新究竟能否上架#热更新究竟能否上架?":"您可能听说过各种说法,但大量实践表明,热更新目前能够顺利上架 AppStore 和其他各种应用市场。唯一需要注意的是,在审核期间请不要发布热更新,不要让审核人员看到各种更新相关的提示和弹窗,即可顺利通过。","是否可以在海外使用#是否可以在海外使用?":"可以的,国内外都有高速 CDN 节点。","哪些修改可以热更新哪些不能#哪些修改可以热更新?哪些不能?":"我们把对应用的修改分为两类:\n不可热更新 —— 原生修改,即所有需要编译后才能生效的修改:\n任何在 iOS 或者 Android 目录中的修改、增删。\n任何含有原生代码的第三方组件的更新、修改。\n可以热更新 —— 非原生修改,即所有无需编译,刷新即可生效的修改:\njs 代码修改,包括第三方纯 js 组件的更新、修改。\n可以在 js 代码中 require/import 的资源文件,例如图片。\n需要注意的是,即便资源文件可以热更新,但这些热更新后的资源文件会以file://协议的形式提供访问,某些读取资源文件的第三方可能并不支持file://协议。","我是否可以搭建自己的热更新服务#我是否可以搭建自己的热更新服务?":"你可以单独使用本组件的原生部分(不包括 js 模块)和命令行工具中的bundle、diff、diffFromIpa、diffFromApk四个功能。这些功能都不会使用我们的热更新服务,也无需注册或登录账号。但你可能要编写自己的 js 模块来与不同的热更新服务器通讯。如果您有兴趣搭建私有云服务,可以邮件联系我们。","热更新成功完成但是重启后又回滚了是怎么回事#热更新成功完成,但是重启后又回滚了是怎么回事?":"可以正常更新,但是重启后回滚,一般有两种可能的情况:\n没有正确配置 bundleUrl\n(仅有 v10 以下版本存在此可能性,v10 及更高版本不存在此情况)没有正确调用 markSuccess\n如果你确定上述两个步骤都正确无误,请在issues 区给我们留言反馈。","热更新报错热更新已暂停原因编译时间戳与服务器记录不一致#热更新报错:“热更新已暂停,原因:编译时间戳与服务器记录不一致。”":"当您每次编译产生一个原生包时,其中都会记录一个编译时间戳buildTime(可以使用pushy parseIpa a.ipa或是pushy parseApk a.apk命令来查看)。如果您需要把这个包发给客户并希望使用热更新功能,那么就需要使用 upload 命令来上传到我们的服务器,而服务器端会记录这个包的版本号和编译时间戳以便后续比对记录。举例来说,假如我已经上传了一个 1.0 版本原生包(1A包),但后来由于种种原因我又重新打包了(1B包)但没有上传,这两个包有相同的版本号,但会有不同的编译时间戳。此时有几种可能的情况:\n我忘记上传过1A包,1B包没有实质更改且还没有客户安装。此时应当放弃1B包,只让客户下载安装1A包。\n我忘记上传过1A包,又生成了1B包且已有客户安装。此时1A包客户可以获取热更新,1B包客户无法获取热更新。要使两批客户都能获取热更新,要么想办法通知1B包客户装回1A包,要么重新发布一个高于1.0版本的原生包且上传到 pushy 服务器,并删除1.0版本的记录,此时1A包和1B包的客户都会收到版本过期需要下载新版本的通知。\n我知道上传过1A包,但我有原生方面的修改需要打了一个新的1B包。此时若将1B包上传到 pushy 服务器会提示版本验证错误,因为已经存在有相同的1.0版本。所以需要首先更改原生版本号再次打包,并首先将此新版本上传到服务器,然后分发给客户。这样1.0版本和新版本可以并存且都可以获得热更新。\n请谨记,任何时候生成原生包,请 1)先修改原生版本号,2)并上传到 pushy 服务器,才可以正常获得热更新。\n如果您是高级版、专业版或大客户VIP版客户,也可以考虑在管理后台的应用设置中启用忽略编译时间戳。此选项仅对比版本号,不校验时间戳,可提供更宽容的热更策略,但可能消耗更多流量,适合需要管理多个分发渠道的客户。","付费问题#付费问题":"","我应该选择哪个业务版本呢#我应该选择哪个业务版本呢?":"其实完全无需考虑,先从最高配额的专业版开始体验(新用户注册验证通过后,将自动获得 7 天专业版试用),各个版本的使用流程完全一致!当试用时间到期后,若当前版本的配额不能满足需求,可随时补差价升级到更高配额的版本。升级自动按天数计算差价,绝无额外费用。目前仅有一项额外的付费专用功能,即忽略编译时间戳设置(在管理后台的应用设置中),此选项仅对比版本号,不校验时间戳,可提供更宽容的热更策略,但可能消耗更多流量,适合需要管理多个分发渠道的客户。此功能目前仅支持高级版、专业版和大客户VIP版。","我可以试用付费版本先评估一下效果吗#我可以试用付费版本先评估一下效果吗?":"新用户注册验证通过后,将自动获得 7 天专业版试用。如您需要更长的评估时间,可将注册用户名、邮箱、公司(或个人)名称发送至 hi@charmlot.com ,标题注明“pushy 评估试用延长”,我们会为您酌情延长评估时间。","配额具体如何计算#配额具体如何计算?":"配额主要分为 4 种:\n可创建的应用数量,注意 iOS 和 Android 版本记做不同的应用。\n原生包数量及大小,不同应用分开计算。原生包指通过pushy uploadIpa/uploadApk命令上传到 Pushy 服务器上作为热更新起始版本的完整 apk/ipa 安装包。\n热更包数量及大小,不同应用分开计算。热更包指通过pushy bundle所命令生成的 ppk 文件。注意这不是用户实际下载的更新文件,用户下载的是通过比对生成的增量 diff 文件(比 ppk 文件要小得多)。\n每日总热更查询次数,所有应用累加计算。每次用户打开应用时,会向服务器查询是否有新的热更新。这个查询次数会被计入每日总热更查询次数。当日超过限额次数后,接口将不再返回新的热更新信息,直到次日重置。\n这里有一些对包大小的优化建议可供参考。\n以上 2、3 条为账户下每个应用单独计算,第 4 条为账户下所有应用累加计算。具体配额和费用由于运营成本可能会有调整,请以价格表页面为准。已购买的用户在续费前不受调整影响。","达到配额后还可以正常使用吗#达到配额后还可以正常使用吗?":"当应用、原生包、热更包数量达到配额限制后,您将无法执行相应的新增操作。但用户端仍可正常检查更新,下载已发布的更新。您可选择升级到更高配额,或是通过删除已不再使用的应用、原生包、热更包来使数量降低到配额以内,以便继续执行新增操作。\n若原生包、热更包大小超过限额,则会导致上传失败。您可以选择升级到更高配额,或是想办法进行精简优化。\n若当日热更检查次数超过限额,则会导致接口返回空数据。您可以选择升级到更高配额,或是优化热更策略,减少用户端检查更新的频率。","我可以单独升级某项配额吗#我可以单独升级某项配额吗?":"我们暂时无法为某个单项配额提供升级选项,请选择更高版本的配额。如果专业版配额仍然不能满足需求,请通过邮件 hi@charmlot.com 与我们联系,我们将根据您的个性化需求为您定制方案。","付费业务到期后不续费现有应用还能否正常使用#付费业务到期后不续费,现有应用还能否正常使用?":"到期后按免费版额度执行。如您现有应用已超出配额,则必须通过删除操作降低到配额以内,方可执行新的操作(如创建应用,上传热更等),但不影响用户获取之前已发布的热更。","如何获取付费的专人技术支持#如何获取付费的专人技术支持?":"请将您的用户名和订单截图发送至 hi@charmlot.com ,同时提供您希望的技术联系方式(QQ、微信等),核实后会有技术专员添加您为好友。","如何开具发票#如何开具发票?":"请将具体开票需求发送至 hi@charmlot.com ,并附上注册邮箱和订单截图。我们默认会回复普通电子发票到注册邮箱,类目为软件服务。如需指定接受邮箱或手机,请在邮件正文中注明。如需要邮寄纸质发票请注明邮寄地址,邮费为到付。\n如邮件发送后三个工作日内未收到回复,请联系 QQ 客服 34731408。","可以使用银行转账付款吗#可以使用银行转账付款吗?":"可以的。请对照价格表,将对应版本的款项转账至:\n公司名称\t武汉青罗网络科技有限公司\t开户行名称\t浙江网商银行\t开户地区\t浙江省杭州市\t支行名称\t浙江网商银行股份有限公司\t账号\t8888888048825564\t\n转账完成后请截图发送至 hi@charmlot.com ,并写明注册邮箱,我们将在一个工作日内开通对应服务。","可以按月付款吗#可以按月付款吗?":"需要月付的客户请联系 QQ 客服 34731408,月付价格为年付价格 / 8。如标准版当前价格为 800 元每年,则月付价格为 100 元每月。\n月付客户需累计支付满 500 元后才能开具发票。大客户VIP版不支持月付。","大客户方案#大客户方案":"如果您的应用有大规模的活跃用户,日均所有应用累计总查询次数超过 100 万次,则建议选择我们专门针对大规模应用优化的大客户VIP版。\n大客户VIP版在应用数量、原生包数量、热更包数量等方面配额与专业版相同。当前价格如下表:\n服务版本\t日均总热更查询次数\t价格(元/年)\tVIP1\t100 万至 1000 万次\t30000\tVIP2\t1000 万至 5000 万次\t60000\tVIP3\t5000 万至 1 亿次\t120000\tVIP4\t1 亿次以上\t请联系客服\t\n具体试用和对接请联系 QQ 客服 34731408"}},"/docs/getting-started":{"title":"安装配置","data":{"":"请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里\n首先你应该有一个基于 React Native 开发的应用,我们把具有 package.json 的目录叫做你的应用根目录。\n如果你还没有初始化应用,请参阅开始使用 React Native。我们假设你已经拥有了开发 React Native 应用的一切环境,包括Node.js、Xcode、Android SDK等等。","安装#安装":"在你的项目根目录下运行以下命令:\n# 先全局安装命令行工具,每台电脑只用装一次\nnpm i -g react-native-update-cli\n# 然后在项目目录中安装热更新模块\nnpm i react-native-update\n# 然后进入ios目录安装iOS模块\ncd ios && pod install\n如果下载极慢或者显示网络失败,请设置使用淘宝镜像 npx nrm use taobao\n注意请不要混用npm/yarn/pnpm等包管理器及对应的lock文件,团队成员请坚持使用同一包管理器,且仅保留统一格式的lock文件\n请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(使用 npx\nreact-native run-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。","手动-link#手动 link":"如果 RN 版本 >= 0.60 则不需要此手动 link 步骤。\n注意:如果是混编 RN\n项目,或monorepo,或任何其他自定义的情况,由于自定义的配置可能不完整或不适应RN的目录结构,导致自动\nlink 的功能可能不能正常工作。此时即便 RN 版本 >= 0.60,你可能也需要执行手动\nlink 操作。","ios#iOS":"RN < 0.60且使用CocoaPods(推荐)\n在 ios/Podfile 中添加\npod 'react-native-update', path: '../node_modules/react-native-update'\n在项目的 ios 目录下运行pod install\n重新编译\nRN < 0.60且不使用CocoaPods\n在 XCode 中的 Project Navigator 里,右键点击Libraries ➜ Add Files to [你的工程名]\n进入node_modules ➜ react-native-update ➜ ios 并选中RCTPushy.xcodeproj`\n在 XCode 中的 project navigator 里,选中你的工程,在 Build Phases ➜ Link Binary With Libraries 中添加 libRCTPushy.a、libz.tbd、libbz2.1.0.tbd\n继续在Build Settings里搜索Header Search Path,添加$(SRCROOT)/../node_modules/react-native-update/ios,勾选recursive。\n在Build Phases添加一个New Run Script Phase运行脚本,内容如下\n#!/bin/bash\nset -x\nDEST=\"../node_modules/react-native-update/ios/\"\ndate +%s > \"$DEST/pushy_build_time.txt\"\n尝试编译一下,顺利的话就会在../node_modules/react-native-update/ios/文件夹下面生成一个pushy_build_time.txt文件。然后在Copy Bundle Resources里把生成的pushy_build_time.txt文件添加进去。","android#Android":"RN < 0.60 或其他不能自动 link 的情况\n在android/settings.gradle中添加如下代码:\ninclude ':react-native-update'\nproject(':react-native-update').projectDir = new File(rootProject.projectDir, \t'../node_modules/react-native-update/android')\n在android/app/build.gradle的 dependencies 部分增加如下代码:\nimplementation project(':react-native-update')\n打开android/app/src/main/java/[...]/MainApplication.java,\n在文件开头增加 import cn.reactnative.modules.update.UpdatePackage;\n在getPackages() 方法中增加 new UpdatePackage()(注意上一行可能要增加一个逗号)","配置-bundle-url#配置 Bundle URL":"注意此步骤无论任何版本,目前都需要手动配置。","ios-1#iOS":"在你的 AppDelegate.mm 或 AppDelegate.m 文件(不同 RN 版本可能后缀名不同)中增加如下代码:\n// ... 其它代码\n#import \"AppDelegate.h\"\n#import \"RCTPushy.h\" // <-- import头文件,注意要放到if条件外面\n// 可能项目里有一些条件编译语句,例如有些版本RN自带的flipper\n// #if DEBUG\n// 注意**不要**在这里面引入\"RCTPushy.h\"\n// #import \n// ...\n// #endif\n// 修改sourceURLForBridge方法\n- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge\n{\n#if DEBUG\n // 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新)\n return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@\"index\"];\n#else\n return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle\n#endif\n}","android-1#Android":"在 MainApplication 中增加如下代码(如果是混编原生的项目或其他原因没有使用 ReactApplication,请使用此 api 集成):\n// ... 其它代码\n// ↓↓↓请注意不要少了这句import\nimport cn.reactnative.modules.update.UpdateContext;\n// ↑↑↑\nclass MainApplication : Application(), ReactApplication {\n override val reactNativeHost: ReactNativeHost =\n object : DefaultReactNativeHost(this) {\n // ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!\n override fun getJSBundleFile(): String? {\n return UpdateContext.getBundleUrl(this@MainApplication)\n }\n // ↑↑↑\n // ...其他代码\n }\n}\n// ... 其它代码\n// ↓↓↓请注意不要少了这句import\nimport cn.reactnative.modules.update.UpdateContext;\n// ↑↑↑\npublic class MainApplication extends Application implements ReactApplication {\n private final ReactNativeHost mReactNativeHost =\n // 老版本 RN 这里可能是 new ReactNativeHost(this)\n new DefaultReactNativeHost(this) {\n // ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!\n @Override\n protected String getJSBundleFile() {\n return UpdateContext.getBundleUrl(MainApplication.this);\n }\n // ↑↑↑\n // ...其他代码\n }\n}\n请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(npx react-native\nrun-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。","禁用-android-的-crunch-优化#禁用 android 的 crunch 优化":"android 会在生成 apk 时自动对 png 图片进行压缩,此操作既耗时又影响增量补丁的生成。为了保证补丁能正常生成,您需要在android/app/build.gradle中关闭此操作:\n...\nandroid {\n ...\n signingConfigs { ... }\n buildTypes {\n release {\n ...\n // 添加下面这行以禁用crunch\n crunchPngs false\n }\n }\n}\n...","登录与创建应用#登录与创建应用":"首先请在 https://pushy-admin.reactnative.cn 注册帐号,然后在你的项目根目录下运行以下命令:\n$ pushy login\nemail: <输入你的注册邮箱>\npassword: <输入你的密码>\n这会在项目文件夹下创建一个.update文件,注意不要把这个文件上传到 Git 等 CVS 系统上。你可以在.gitignore末尾增加一行.update来忽略这个文件。登录之后可以创建应用。注意 iOS 平台和安卓平台需要分别创建:\n$ pushy createApp --platform ios\nApp Name: <输入应用名字>\n$ pushy createApp --platform android\nApp Name: <输入应用名字>\n两次输入的名字可以相同,这没有关系。\n如果你已经在网页端或者其它地方创建过应用,也可以直接选择应用:\n$ pushy selectApp --platform ios\n1) 鱼多多(ios)\n2) 招财旺(ios)\nTotal 2 ios apps\nEnter appId: <输入应用前面的编号>\n选择或者创建过应用后,你将可以在文件夹下看到update.json文件,其内容类似如下形式:\n{\n \"ios\": {\n \"appId\": 1,\n \"appKey\": \"<一串随机字符串>\"\n },\n \"android\": {\n \"appId\": 2,\n \"appKey\": \"<一串随机字符串>\"\n }\n}\n你可以安全的把update.json上传到 Git 等 CVS 系统上,与你的团队共享这个文件,它不包含任何敏感信息。当然,他们在使用任何功能之前,都必须首先输入pushy login进行登录。至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅代码集成。"}},"/docs/integration":{"title":"代码集成","data":{"":"请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里\n安装配置完成后,确定应用编译顺利通过,下面我们来进行代码集成。","获取-appkey#获取 appKey":"检查更新时必须提供你的appKey,这个值保存在update.json中(使用pushy createApp或pushy selectApp命令后会自动生成),并且根据平台不同而不同。你可以用如下的代码获取appKey:\nimport { Platform } from \"react-native\";\nimport _updateConfig from \"./update.json\";\nconst { appKey } = _updateConfig[Platform.OS];\n如果你不使用 pushy 命令行,也可以从网页端查看到两个应用 appKey,并根据平台的不同来选择。","初始化服务#初始化服务":"import { PushyProvider, Pushy } from \"react-native-update\";\n// 唯一必填参数是appKey,其他选项请参阅 api 文档\nconst pushyClient = new Pushy({\n appKey,\n // 注意,默认情况下,在开发环境中不会检查更新\n // 如需在开发环境中调试更新,请设置debug为true\n // 但即便打开此选项,也仅能检查、下载热更,并不能实际应用热更。实际应用热更必须在release包中进行。\n // debug: true\n});\n// 在根组件外加上PushyProvider后导出\nexport default function Root() {\n // 注意,在使用 PushyProvider 的当前组件中,无法直接调用 usePushy\n // 只有当前组件的子组件才能调用 usePushy\n return (\n \n {/* ↓ 整个应用的根组件放到PushyProvider中 */}\n \n \n );\n}\n如没有特别的自定义需求,那么到此热更新已经可以开始正常运作(如需在应用内执行 apk 更新,还需配置安装权限)。默认配置下,在 App 启动,以及从后台切换到前台时会触发更新检查,弹出提示的内容也固定。如需自定义触发时机,以及修改界面提示等,请参考下面的自定义更新界面。","自定义更新界面#自定义更新界面":"默认配置下,pushy 会以系统 alert 的形式来弹出更新提示,如需自定义更新界面,首先请关闭默认的 updateStrategy 更新策略,并打开 debug 选项以便调试:\nconst pushyClient = new Pushy({\n appKey,\n+ updateStrategy: null,\n+ debug: true,\n});\n所有更新相关的数据可以通过一个单一的usePushy()hook 函数来获取,然后可以根据其提供的数据来自行渲染自定义的界面,如下面的例子:\nimport { Icon, PaperProvider, Snackbar, Banner } from \"react-native-paper\";\nfunction App() {\n const {\n client,\n checkUpdate,\n downloadUpdate,\n switchVersionLater,\n switchVersion,\n updateInfo,\n packageVersion,\n currentHash,\n progress: { received, total } = {},\n } = usePushy();\n const [showUpdateBanner, setShowUpdateBanner] = useState(false);\n const [showUpdateSnackbar, setShowUpdateSnackbar] = useState(false);\n const snackbarVisible =\n showUpdateSnackbar && updateInfo?.update;\n return (\n \n \n 更新下载进度:{received} / {total}\n \n \n 点击这里检查更新\n \n {snackbarVisible && (\n {\n setShowUpdateSnackbar(false);\n }}\n action={{\n label: \"更新\",\n onPress: async () => {\n setShowUpdateSnackbar(false);\n await downloadUpdate();\n setShowUpdateBanner(true);\n },\n }}\n >\n 有新版本({updateInfo.name})可用,是否更新?\n \n )}\n {\n switchVersionLater();\n setShowUpdateBanner(false);\n },\n },\n ]}\n icon={({ size }) => (\n \n )}\n >\n 更新已完成,是否立即重启?\n \n \n );\n}\n其中checkUpdate方法可以用来手动触发更新检查。检查后会更新usePushy返回的updateInfo(注意checkUpdate方法本身没有返回值),有三种情况:\n{expired: true}:该应用原生包已过期(三种情况:1. 主动设置为过期状态,2. 主动删除,3. 从未上传),开发者应该在 pushy 的管理后台添加一个更新下载链接,并自行提示用户下载。如需在应用内执行 apk 更新,还需配置安装权限。\n{upToDate: true}:当前已经更新到最新,无需进行更新。\n{update: true}:当前有新版本可以更新。info 的name、description字段可以用于提示用户,而metaInfo字段则可以根据你的需求自定义其它属性(如是否静默更新、是否强制更新等等),具体用法可参考场景实践。另外还有几个字段,包含了补丁包的下载地址等。 pushy 会首先尝试耗费流量更少的更新方式。\n当返回的updateInfo中update字段为 true 时,即可调用downloadUpdate方法来下载更新,此时可以获取到下载的进度数据progress。下载完成后可以调用switchVersion来立即重启更新,也可以使用switchVersionLater来标记下次启动时更新。","统计数据#统计数据":"初始化 Pushy 客户端时可以传入自定义的 logger 函数,其中可以自己记录日志或上报统计数据,比如下面的例子使用 Google Analytics 来上报事件:\nimport { getAnalytics, logEvent } from \"firebase/analytics\";\nconst analytics = getAnalytics();\nconst pushyClient = new Pushy({\n appKey,\n logger: ({ type, data }) => {\n logEvent(analytics, \"pushy_\" + type, data);\n },\n});\n以上提及的所有 api 的说明文档可在这里查看。现在,你的应用已经可以通过 pushy 服务检查版本并进行更新了。下一步,你可以开始尝试发布应用包和版本,请参阅发布热更新。"}},"/docs/publish":{"title":"发布热更新","data":{"":"现在你的应用已经具备了检测更新的功能,下面我们来尝试发布并更新它。流程可参考下图:\n流程总结如下:\n我们需要先打包一个原生 release 版本,在打包前请确保已集成了react-native-update并在调试过程中运行正常,安卓端关闭了crunchPngs设置,打包说明可参考iOS 打包和android 打包。打包完成后请使用pushy uploadIpa或者pushy uploadApk命令来把这个安装包上传到 pushy 服务器端,以作为之后热更差量对比的基准。同时请保留好这个安装包,上架和分发给用户所使用的安装包需要和服务器端完全一致。建议使用 git tag 功能来标记原生版本号(例如v1.0.0)。\n然后在基准版本之上迭代业务逻辑(增删 js 代码,增删图片等静态资源),使用pushy bundle命令来生成和发布热更新版本,而不需要重新打包。建议使用 git tag 功能来标记热更版本号(例如v1.0.1)。\n如果迭代过程中有原生方面的修改,则需要发布并上传新的原生基准版本(重复步骤 1,但需要设置不同的原生版本号)。可以只保留一个原生基准版本,也可以多版本同时维护。","发布原生基准版本#发布原生基准版本":"","ios#iOS":"首先参考文档-在设备上运行,确定你正在使用离线包。然后点击菜单。按照正常的发布流程打包.ipa文件:\nXcode 中运行设备选真机或 Generic iOS Device\n菜单中选择 Product - Archive\nArchive 完成后选择Export生成.ipa 文件\n然后运行如下命令上传到 pushy 服务器以供后续版本比对之用\n$ pushy uploadIpa \n此 ipa 的CFBundleShortVersionString字段(位于ios/项目名/Info.plist中)会被记录为原生版本号packageVersion。随后你可以选择往 AppStore 上传这个版本(可以重新 export 并调整相关选项,但请不要重新 archive),也可以先通过Test flight或蒲公英等渠道进行真机安装测试。请注意:暂不支持通过 Xcode 直接进行热更新测试。如果后续需要再次 archive 打包(例如修改原生代码或配置。如果只是修改 js 代码则不需要重新打包。),请先更改版本号,并在打包完成后再次uploadIpa到服务器端记录,否则后续生成的相同版本的原生包会由于编译时间戳不一致而无法获取热更新。\n注意:如果你在上传之前就运行了新的原生版本,由于服务器端没有记录,会暂停其更新数小时。可删除原先安装的 app 再重新安装以清空暂停设置。在上传之后安装的客户端不会受此影响。","android#Android":"首先参考文档-打包 APK设置签名,然后在 android 文件夹下运行./gradlew assembleRelease或./gradlew aR,你就可以在android/app/build/outputs/apk/release/app-release.apk中找到你的应用包。\n如果你需要使用 aab 格式(android app bundle,google 市场专用)的包,请参考这里的做法将其转换为 apk 格式后再操作。\n然后运行如下命令\n$ pushy uploadApk android/app/build/outputs/apk/release/app-release.apk\n即可上传 apk 以供后续版本比对之用。此 apk 的versionName字段(位于android/app/build.gralde中)会被记录为原生版本号packageVersion。随后你可以选择往应用市场发布这个版本,也可以先往设备上直接安装这个 apk 文件以进行测试。如果后续需要再次打包(例如修改原生代码或配置。如果只是修改 js 代码则不需要重新打包。),请先更改版本号,并再次uploadApk到服务器端记录,否则后续生成的相同版本的原生包会由于编译时间戳不一致而无法获取热更新。\n注意:如果你在上传之前就运行了新的原生版本,由于服务器端没有记录,会暂停其更新数小时。可删除原先安装的 app 再重新安装以清空暂停设置。在上传之后安装的客户端不会受此影响。","发布热更新版本#发布热更新版本":"你可以尝试修改一行代码(譬如将版本一修改为版本二),然后使用pushy bundle --platform 命令来生成新的热更新版本。\n$ pushy bundle --platform android\nBundling with React Native version: 0.22.2\n<各种进度输出>\nBundled saved to: build/output/android.1459850548545.ppk\nWould you like to publish it?(Y/N)\n如果想要立即上传,此时输入 Y。当然,你也可以在将来使用pushy publish --platform android build/output/android.1459850548545.ppk来上传刚才打包好的热更新包。\n Uploading [========================================================] 100% 0.0s\nEnter version name: <输入热更新版本名字,如1.0.0-rc>\nEnter description: <输入热更新版本描述>\nEnter meta info: {\"ok\":1}\nOk.\nWould you like to bind packages to this version?(Y/N)\n此时版本已经提交到 pushy 服务,但用户暂时看不到此更新,你需要先将特定的原生包版本绑定到此热更新版本上。此时输入 Y 立即绑定,你也可以在将来使用pushy update --platform 来对已上传的热更包和原生包进行绑定。除此以外,你还可以在网页端操作,简单的将对应的原生包版本拖到需要的热更新版本下即可。\n┌────────────┬──────────────────────────────────────┐\n│ Package Id │ Version │\n├────────────┼──────────────────────────────────────┤\n│ 46272 │ 2.0(normal) │\n├────────────┼──────────────────────────────────────┤\n│ 45577 │ 1.0(normal) │\n└────────────┴──────────────────────────────────────┘\n共 2 个包\n输入原生包 id: 46272\n版本绑定完毕后,服务器会在几秒内生成差量补丁,客户端就可以获取到更新了。后续要继续发布新的热更新,只需反复执行pushy bundle命令即可,不需要重新打包。恭喜你,至此为止,你已经完成了植入代码热更新的全部工作。","测试发布与回滚#测试、发布与回滚":"我们强烈建议您先发布一个测试包,再发布一个除了版本号以外均完全相同的正式包。例如,假设我们有一个正式包,版本为1.6.0,那么可以修改版本号重新打包一个1001.6.0,以一个明显不太正常的版本号来标识它是一个测试版本,同时后几位相同,可以表明它和某个正式版本存在关联(内容/依赖一致)。在每次往发布包发起热更新之前,先对测试包1001.6.0进行更新操作,基本测试通过之后,再在网页后台上将热更包重新绑定到正式包1.6.0上。如果在测试包中发现了重大问题,你就可以先进行修复,更新测试确认通过后再部署到正式线上环境。这样,可以最大程度的避免发生线上事故。万一确实发生线上事故需要回滚的话,首先利用版本控制系统回滚代码到正常的状态,然后重新生成热更包并推送即可。"}},"/":{"title":"Index","data":{}},"/pricing":{"title":"价格","data":{}}} \ No newline at end of file +{"/docs/api":{"title":"API参考","data":{"javascript-方法#JavaScript 方法":"","new-pushyoptions-pushyoptions#new Pushy(options: PushyOptions)":"创建 Pushy 热更新服务实例,其构造参数如下:\ninterface PushyOptions {\n // 必填,通过pushy createApp或selectApp命令,或在网页管理端获取\n appKey: string;\n // 如已购买私有部署服务,可在此自定义私有服务器地址\n server?: {\n // 主节点\n main: string;\n // 备用节点群\n backups?: string[];\n // 远程查询节点接口\n queryUrl?: string;\n };\n // 自定义日志输出,也可用于上报统计数据\n logger?: ({ type, data }: { type: EventType; data: EventData }) => void;\n // 触发自动检查更新的策略\n checkStrategy?:\n | \"onAppStart\" // 仅在app启动时\n | \"onAppResume\" // 仅在app从后台切换到前台时\n | \"both\"; // 默认值,同时包含前两个场景\n | null; // 不自动检查更新,必须手动调用checkUpdate方法,此选项需 v10.4.2+ 版本\n // 自动下载和应用更新的策略\n updateStrategy?:\n | \"alwaysAlert\" // 调试环境(__DEV__)默认值,使用系统默认的alert页面提示热更且会在有报错时弹出提示\n | \"alertUpdateAndIgnoreError\" // 生产环境默认值,在有热更时使用系统默认的alert页面提示热更,但不弹出任何报错提示\n | \"silentAndNow\" // 自动静默下载并立刻应用热更\n | \"silentAndLater\"; // 自动静默下载,但仅在用户退出app后重启时应用更新\n | null; // 不自动下载和应用更新,如需自定义热更界面请选择此项\n // 是否在热更重启后自动标记为成功,默认为true\n autoMarkSuccess?: boolean;\n // 是否在若干ms后自动清除最后的报错,默认为不清除\n dismissErrorAfter?: number;\n // 是否在开发环境中检查热更,默认为false。如需在开发环境中调试热更,请打开此选项。\n // 但即便打开此选项,也仅能检查、下载热更,并不能实际应用热更。实际应用热更必须在release包中进行。\n // 此选项需 v10.4.2+ 版本\n debug?: boolean;\n // 是否在调用 checkUpdate 和 downloadUpdate 时抛出错误,默认为不抛出错误,通过 lastError 获取错误信息\n // 启用后可以使用 try catch 语句 捕获错误,同时 lastError 也仍然可用\n // try {\n // await checkUpdate();\n // } catch (e) {\n // console.error(e);\n // }\n // 此选项需 v10.8.0+ 版本\n throwError?: boolean;\n // 在检查更新前执行,返回 false 则取消检查更新\n // 此选项需 v10.12.0+ 版本\n beforeCheckUpdate?: () => Promise;\n // 在下载更新前执行,返回 false 则取消下载更新,可以配合自定义的 metaInfo 做一些条件控制\n // 此选项需 v10.12.0+ 版本\n beforeDownloadUpdate?: (info: UpdateInfo) => Promise;\n}\n// 日志事件类型\ntype EventType =\n // 更新失败,重启后发生回滚\n | \"rollback\"\n // 检查更新时报错\n | \"errorChecking\"\n // 正在发起检查\n | \"checking\"\n // 正在下载更新\n | \"downloading\"\n // 更新失败\n | \"errorUpdate\"\n // 更新成功\n | \"markSuccess\"\n // 下载apk\n | \"downloadingApk\"\n // 下载apk前申请存储权限被用户拒绝\n | \"rejectStoragePermission\"\n // 下载apk前申请存储权限发生错误\n | \"errorStoragePermission\"\n // 下载apk时发生错误\n | \"errorDownloadAndInstallApk\";\n// 日志事件数据\ninterface EventData {\n // 当前已完成的热更hash值,如尚未热更则为空字符串\n currentVersion: string;\n // 客户端版本信息\n cInfo: {\n pushy: string; // 当前pushy版本\n rn: string; // 当前rn版本\n os: string; // 当前操作系统及版本\n uuid: string; // 用户标识符\n };\n // 客户端原生版本号\n packageVersion: string;\n // 编译时间戳\n buildTime: number;\n // 报错相关的信息\n message?: string;\n // 发生回滚的版本hash值\n rolledBackVersion?: string;\n // 更新失败的新版本hash值\n newVersion?: string;\n // 其他一些数据\n [key: string]: any;\n}","usepushy#usePushy()":"热更相关的工具函数。\n注意,在使用 的当前组件(一般是根组件)中无法直接调用usePushy,只有当前组件的子组件才能调用。\nconst {\n checkUpdate,\n switchVersion,\n switchVersionLater,\n markSuccess,\n dismissError,\n downloadUpdate,\n downloadAndInstallApk,\n getCurrentVersionInfo,\n parseTestQrCode,\n currentHash,\n packageVersion,\n client,\n progress,\n updateInfo,\n lastError,\n} = usePushy();\n其类型定义和功能如下:\ninterface PushyContext {\n // 检查更新\n checkUpdate: () => Promise;\n // 下载热更完成后调用,立即重启切换新版本\n switchVersion: () => void;\n // 下载热更完成后调用,用户手动重启app后切换新版本(静默更新)\n switchVersionLater: () => void;\n // 热更完成重启后,手动标记热更完成\n markSuccess: () => void;\n // 清除最后的报错状态\n dismissError: () => void;\n // 下载热更\n downloadUpdate: () => Promise;\n // 下载并安装apk\n downloadAndInstallApk: (url: string) => Promise;\n // 获取当前已热更版本的信息\n getCurrentVersionInfo: () => Promise<{\n name?: string;\n description?: string;\n metaInfo?: string;\n }>;\n // 解析测试二维码,此方法需 v10.11.2+ 版本\n parseTestQrCode: (qrCode: string) => void;\n // 当前的版本hash\n currentHash: string;\n // 当前的原生版本号\n packageVersion: string;\n // 当前的pushy热更服务示例\n client?: Pushy;\n // 下载开始后的进度数据\n progress?: {\n hash: string;\n // 已下载的字节数\n received: number;\n // 待下载的总字节数\n total: number;\n };\n // 热更相关信息\n updateInfo?: {\n // 已是最新版本,无需热更\n upToDate?: true;\n // 当前原生版本已过期,需要下载新的原生版本\n expired?: true;\n // 在pushy网页管理端设置的原生版本下载地址\n downloadUrl?: string;\n // 是否存在新的热更\n update?: true;\n // 新热更的版本名称\n name?: string;\n // 新热更的hash值\n hash?: string;\n // 新热更的更新说明\n description?: string;\n // 新热更携带的额外元数据\n metaInfo?: string;\n // 当前热更是否已暂停\n paused?:\n | \"app\" // 当前应用所有原生版本暂停\n | \"package\"; // 仅当前原生版本暂停\n // 其他信息\n message?: string;\n };\n // 检查、下载、应用热更等过程中的最新一次报错\n lastError?: Error;\n}","async-function-checkupdate#async function checkUpdate()":"触发更新检查,更新usePushy中的updateInfo(注意checkUpdate方法本身没有返回值),返回值有三种情形:\n{expired: true}:该应用原生包已过期(三种情况:1. 主动设置为过期状态,2. 主动删除,3. 从未上传),需要引导用户下载或跳转到应用市场(需要在网页管理端设置中填写downloadUrl)。如需在应用内执行 apk 更新,还需配置安装权限。\n{\n expired: true,\n downloadUrl: 'http://appstore/downloadUrl',\n}\n{upToDate: true}:当前已经更新到最新,无需进行更新。\n{update: true}:当前有新版本可以更新。name、description字段可以用于展示给用户版本号,更新内容等信息,而metaInfo字段则可以根据你的需求自定义一些标记(如是否静默更新、是否强制更新等等,自己根据标记的属性做一些条件流程控制),具体用法可参考场景实践。另外还有几个字段,包含了热更新文件的下载地址,\n{\n update: true,\n name: '1.0.3-rc',\n hash: 'hash',\n description: '添加聊天功能\\n修复商城页面BUG',\n metaInfo: '{\"silent\":true}',\n pdiffUrl: 'http://update-packages.reactnative.cn/hash',\n diffUrl: 'http://update-packages.reactnative.cn/hash',\n}","async-function-downloadupdate#async function downloadUpdate()":"下载热更包。仅当update:true时实际进行下载。会更新progress数据。","async-function-downloadandinstallapkurl#async function downloadAndInstallApk(url)":"下载更新的 apk 包并直接安装。url必须为可直接下载到 apk 文件的地址。注意要使用这个功能还需要在AndroidManifest.xml中手动添加安装权限,如果需要考虑 Android 7.0 以下的客户,则还需要添加外部存储权限。\n\n\n\n注意某些应用市场可能会因为上述权限拒绝应用上架。去掉上述两个权限并不影响热更新功能。","function-marksuccess#function markSuccess()":"手动调用此函数作为更新成功的标记(否则下次启动会默认失败自动回滚)。默认情况下不需调用此函数,除非设置autoMarkSuccess为false。","async-function-getcurrentversioninfo#async function getCurrentVersionInfo()":"获取当前已热更版本的信息(如尚未热更过则返回空对象)。返回值示例:\n{\n name: '1.0.3-rc',\n description: '添加聊天功能\\n修复商城页面BUG',\n metaInfo: '{\"silent\":true}',\n}","function-switchversion#function switchVersion()":"立即重启应用,并加载已经下载完毕的版本。","function-switchversionlater#function switchVersionLater()":"在下一次启动应用的时候加载已经下载完毕的版本。","function-parsetestqrcodeqrcode-string#function parseTestQrCode(qrCode: string)":"解析测试二维码,一般用于给 QA 人员测试热更新。如果在应用中已有扫码功能,则可以在应用中扫描 pushy 后台的测试二维码来测试任意版本的热更包。注意使用此方法,上述界面中的\"使用 Deep Link\"选项 请不要 勾选。代码示例:\n {\n // 识别到二维码后先关闭相机\n setShowCamera(false);\n // 先解析是否是pushy的测试二维码\n if (parseTestQrCode(codeStringValue)) {\n // 如果是pushy的测试二维码,则不再做其他业务扫码逻辑\n return;\n }\n // 如果不是,继续处理其他业务扫码逻辑\n }}\n/>","android-方法#Android 方法":"","updatecontextsetcustominstancemanagerreactinstancemanager-instancemanager#UpdateContext.setCustomInstanceManager(ReactInstanceManager instanceManager)":"如果是集成/混编 Android 方案,则可以使用此方法传入你自行创建的 ReactInstanceManager。自v5.5.8版本起可用。示例:\nimport cn.reactnative.modules.update.UpdateContext\nmReactInstanceManager = ReactInstanceManager.builder()\n // ...各种setter,但注意不要调用setBundleAssetName\n .setJSBundleFile(UpdateContext.getBundleUrl(mContext, \"assets://index.android.bundle\"))\n .build();\nUpdateContext.setCustomInstanceManager(mReactInstanceManager);"}},"/docs/changelog":{"title":"更新日志","data":{"10542024-04-23#10.5.4(2024-04-23)":"修复 web 端的兼容问题","10422024-04-22#10.4.2(2024-04-22)":"加入 debug 参数,允许在开发环境中调试热更\n热更策略中加入 null 选项,即禁止自动检查,完全手动控制","10102024-02-24#10.1.0(2024-02-24)":"全新设计的 api 接口,全面 hook 化,更易集成和自定义\n全面同时支持新老架构和 hermes","9002023-09-02#9.0.0(2023-09-02)":"初步支持新架构\n添加事件回调以方便统计","8012022-07-05#8.0.1(2022-07-05)":"没有 update.json 文件的情况下不会再报错","7412022-05-04#7.4.1(2022-05-04)":"修复极少数热更失败的情况","7342021-11-04#7.3.4(2021-11-04)":"新增快速集成方法。\n默认使用 AndroidX 支持库(使用npx jetify -r命令转换可以支持老的 support 库)","6402021-10-06#6.4.0(2021-10-06)":"新增查看本地版本信息的方法(getCurrentVersionInfo())","6302021-10-04#6.3.0(2021-10-04)":"库文件体积优化","6202021-08-13#6.2.0(2021-08-13)":"修复 android 7 及以下版本可能解压失败的问题","6102021-07-29#6.1.0(2021-07-29)":"修复频繁调用下载更新导致的图片丢失问题","6022021-05-18#6.0.2(2021-05-18)":"修复 android 7 及以下版本安装 apk 报错的问题","6002021-05-04#6.0.0(2021-05-04)":"换用 hdiff 算法,更新所需流量更少,速度更快\n修复一些少见的崩溃问题","51002020-12-18#5.10.0(2020-12-18)":"提升 iOS 更新的稳定性","5902020-09-27#5.9.0(2020-09-27)":"可在应用内直接下载安装新版本 apk","5832020-09-24#5.8.3(2020-09-24)":"加入下载进度回调","5702020-08-13#5.7.0(2020-08-13)":"初始化时检查 Android 的 bundle url 是否正确配置","5602020-05-26#5.6.0(2020-05-26)":"修复 iOS 更新偶尔报找不到 app.json 的问题","559-2020-04-14#5.5.9 (2020-04-14)":"修复编译时找不到 generateiOSBuildTime.sh 的问题","558-2020-04-02#5.5.8 (2020-04-02)":"提供 setCustomInstanceManager 方法,方便自己集成 RN 的用户调用","556-2020-02-11#5.5.6 (2020-02-11)":"bundle 时清除缓存\n修复更新包过大时可能出现的崩溃\npublish 时检查文件格式","555-2020-01-18#5.5.5 (2020-01-18)":"提升服务健壮性","554-2020-01-13#5.5.4 (2020-01-13)":"防止某些情况下安卓找不到 bundle 文件引起的崩溃","553-2019-12-18#5.5.3 (2019-12-18)":"忽略 hermes 的输出避免 buffer 溢出","552-2019-12-06#5.5.2 (2019-12-06)":"修复使用 use_frameworks 时无法读取时间戳的问题","550-2019-11-24#5.5.0 (2019-11-24)":"打包时加入时间戳","540-2019-11-16#5.4.0 (2019-11-16)":"修改类名方法名","532-2019-10-25#5.3.2 (2019-10-25)":"可以禁用 ios 端以避免审核被拒","530-2019-09-19#5.3.0 (2019-09-19)":"替换 apk reader 以避免某些环境读取 apk 版本号报错的问题","529-2019-09-18#5.2.9 (2019-09-18)":"添加 proguard 混淆规则以解决开启混淆后闪退的问题","528#5.2.8":"解决某些情形下 Android 调用 switchVersion 不能重启的问题","527#5.2.7":"改进 windows 端打更新包的兼容性(部分 windows 机器上会产生空 ppk 文件)","524#5.2.4":"支持 RN 0.61 的 hermes(路径变化)\niOS 端使用第三方的 SSZipArchive 以减少重名冲突","522#5.2.2":"修复一处导致 iOS 回滚的问题","521#5.2.1":"检测如果开启了 hermes,则自动编译为 hermes 字节码格式","520#5.2.0":"添加 typescript 声明\n支持 cocoapods","519#5.1.9":"重写 bundle 命令以提升版本兼容性\n改进命令行的输出样式","518#5.1.8":"服务器迁移到 https\nandroid 支持 64 位","516#5.1.6":"解决 Android 热更新后部分图片丢失问题:\n同一个项目中放置了多个完全相同的文件,在 5.1.0 至 5.1.5 之间的版本中,更新后有时会出现其中的部分无法显示。此问题在 5.1.6 版本修复。修复此问题涉及原生部分,需要重新打包。"}},"/docs/bestpractice":{"title":"场景实践","data":{"优化原生和热更包体积#优化原生和热更包体积":"","ios-原生包优化ipa#iOS 原生包优化(ipa)":"对于同一份 archive(其版本号、编译时间和内置 bundle 已固定,不会受导出方式所影响),可以用不同选项多次导出 ipa,选择其中最小的上传到 pushy 服务器作为热更基准包。","android-原生包优化apk#Android 原生包优化(apk)":"apk 的优化主要考虑两个方向:\n启用 proguard 压缩混淆源码。但这一步可能导致一些使用反射的代码运行时报错,启用后需要充分测试每个页面和功能,以及需要阅读一些第三方关于 proguard 的特别设置说明。\n分开编译不同的 cpu 架构。找到android/app/build.gradle中的 cpu 架构部分,如下所示启用enable选项:\nsplits {\n abi {\n reset()\n- enable enableSeparateBuildPerCPUArchitecture\n+ enable true // 启用单独的 cpu 架构编译\n universalApk false // If true, also generate a universal APK\n }\n}\n如此一来会在编译目录中输出多个 apk 文件,分发和上传到热更新服务时只需要使用app-arm64-v8a-release.apk文件,可以大幅减小 apk 的大小。","热更新包优化ppk#热更新包优化(ppk)":"热更新包的主要内容是 js 包和其所引用的静态资源(主要是图片)。\njs 包成分分析。可以借助一些第三方工具(如react-native-bundle-visualizer)来分析 js 文件中哪些占比较大,是否可以用其他库替换等(如 dayjs 替换 moment,lodash-es 替换 lodash)。\n图片优化。\n在保证体验的情况下,使用一些工具对图片进行裁剪压缩。\n如不需要图片的透明像素,可以考虑将 png 格式转为 jpg 格式。\n还可以进一步考虑压缩比更高的图片格式,例如 webp 格式(需要第三方原生插件例如react-native-webp-format),heif 格式(iOS 11 和 Android 10 以上原生支持)等。这里有个图片格式对比可供参考 https://compare.rokka.io/_compare/#heif=40&jpeg=80&webp=80&av1=40&width=800。","有很多渠道包需要热更如何操作比较方便#有很多渠道包需要热更,如何操作比较方便?":"如果渠道包的js代码和初始资源有差别(无论多么细微的差别都会生成不同的 jsbundle),那么只能单独生成 apk,分别上传和绑定。可以考虑写一些脚本自动调用 cli 来执行批量操作。\n如果渠道包的js代码和初始资源完全一致,可以考虑使用Flavor 构建,或其他一些动态生成渠道包的方案(比如腾讯的 VasDolly,美团的 walle等),这样所有的渠道包基于同一个基础 apk 生成(因而会有相同的编译时间戳和 jsbundle)。这样可以只用上传一个基础 apk,对此 apk 的热更操作可以对所有渠道包生效。\n如果您是高级版、专业版或大客户VIP版客户,也可以考虑在管理后台的应用设置中启用忽略编译时间戳。此选项仅对比版本号,不校验时间戳,可提供更宽容的热更策略,但可能消耗更多流量。","如何支持-aab-格式的原生包#如何支持 aab 格式的原生包?":"如果您需要使用 aab 格式的 android 原生包,那么可以在上传到 Google play 之后,在其控制台中下载转换后的 apk 格式(见下图),然后将这个 apk 包上传到热更新的后台,即可正常支持热更新。","ci-的集成#CI 的集成":"在开发环境中,每次 bundle 都会生成一个不同名字的 ppk 文件,这不利于持续集成(CI)系统的引入。要解决这个问题,你可以使用--output参数来指定输出 ppk 文件的名字和路径,便于进行自动发布。","测试发布与回滚#测试、发布与回滚":"自 v10.11.2 版本开始,可以使用以下两种快捷扫码方案来测试热更,而无需提前进行绑定:\n若应用启用了 DeepLink 功能\n代码中无需任何改动,只需在上述界面中勾选“使用 Deep Link”,填入您应用的协议名,例如\"pushy://\",然后使用系统相机或系统内置的扫一扫功能扫码(注意不能使用微信扫码),即可自动调起应用并触发更新。\n若应用自带扫码功能\n请参考 parseTestQrCode 方法的说明。先发布一个测试包,再发布一个除了版本号以外均完全相同的正式包。例如,假设我们有一个正式包,版本为1.6.0,那么可以修改版本号重新打包一个1001.6.0,以一个明显不太正常的版本号来标识它是一个测试版本,同时后几位相同,可以表明它和某个正式版本存在关联(内容/依赖一致)。在每次往发布包发起热更新之前,先对测试包1001.6.0进行更新操作,基本测试通过之后,再在网页后台上将热更包重新绑定到正式包1.6.0上。如果在测试包中发现了重大问题,你就可以先进行修复,更新测试确认通过后再部署到正式线上环境。这样,可以最大程度的避免发生线上事故。万一确实发生线上事故需要回滚的话,先立即对原生包或者整个应用设置暂停热更,然后更改绑定到之前正常的版本,或者利用版本控制系统回滚代码到正常的状态,然后重新生成热更包并推送。","元信息meta-info的使用#元信息(Meta Info)的使用":"在发布热更新版本时,或者在网页端,你可以编辑版本的元信息。这是一段在检查更新时可以获得的字符串,你可以在其中按你所想的格式(一般建议用JSON 格式)保存一些信息。比如我们可以在元信息中约定字段标志silent,表示需要静默更新。当我们上传热更包填写 metainfo 时,以JSON 格式输入:\n{ \"silent\": true }\n请注意,我们并不对输入做任何格式校验和约束,请自行校验输入是否正确。\n此时在客户端检查更新时,能获取到我们刚刚输入的元信息,但它并不具备任何功能,只是一个字符串而已。所以我们其实需要预先在更新流程中加入对应的处理逻辑:\n// 调用 checkUpdate 获取 updateInfo\nif (updateInfo.expired) {\n // ... 原生包版本过期,下载或跳转下载页面\n} else if (updateInfo.upToDate) {\n // ... 没有更新,弹提示或忽略\n} else {\n // 有更新,一般来说我们在这里给用户弹窗提示,让用户选择是否更新\n // 那么静默更新的本质其实就是不弹窗,直接执行,所以可以在这里加入额外的判断流程\n // ...\n}\n我们在原有的更新流程中加入元信息的读取和判断:\nlet metaInfo = {};\ntry {\n // 注意 JSON 输入有可能有错误,需要用 try 语句来避免应用被带崩\n metaInfo = JSON.parse(updateInfo.metaInfo);\n} catch (e) {\n // 异常处理,忽略或上报?\n}\nif (metaInfo.silent) {\n // 如果热更包携带有 silent 字段,不询问用户,直接执行更新\n switchVersion();\n} else {\n // 否则还是走之前的询问流程\n // Alert.alert('提示', '检查到新的版本.......\n}\n又比如,可能某个版本包含一些重要的公告内容,所以还可以在上面插入一个公告字段等等。如何使用元信息,完全取决于您的想象力!"}},"/docs/cli":{"title":"命令行工具","data":{"安装#安装":"$ npm install -g react-native-update-cli","命令#命令":"","pushy-bundle#pushy bundle":"生成资源包\nplatform: ios|android 对应的平台\nentryFile: 入口脚本文件\nintermediaDir: 临时文件输出目录\noutput: 最终 ppk 文件输出路径\ndev: 是否打包开发版本\nsourcemap: 是否生成 sourcemap(需 cli 版本 1.11.0+)","pushy-parseipa-ipafile#pushy parseIpa [ipaFile]":"解析 ipa 文件并输出一些相关信息,如版本号,编译时间戳等。","pushy-parseapk-apkfile#pushy parseApk [apkFile]":"解析 apk 文件并输出一些相关信息,如版本号,编译时间戳等。","pushy-diff-originnext#pushy diff [origin][next]":"提供两个 ppk 文件,生成从 origin 到 next 版本的差异更新包。\noutput: diff 文件输出路径","pushy-difffromapk-apkfilenext#pushy diffFromApk [apkFile][next]":"提供一个 apk 文件和一个 ppk 文件,生成从 apk 文件到 next 版本的差异更新包。如果使用热更新开放平台,你不需要自己执行此命令。\noutput: diff 文件输出路径","pushy-difffromipa-ipafilenext#pushy diffFromIpa [ipaFile][next]":"提供一个 ipa 文件和一个 ppk 文件,生成从 ipa 文件到 next 版本的差异更新包。如果使用热更新开放平台,你不需要自己执行此命令。\noutput: diff 文件输出路径","pushy-login-emailpwd#pushy login [email][pwd]":"登录热更新开放平台。你需要先登录才能使用下面的命令。","pushy-logout#pushy logout":"登出并清除本地的登录信息","pushy-me#pushy me":"查看自己是否已经登录,以及昵称等信息。","pushy-createapp#pushy createApp":"创建应用并立刻绑定到当前工程。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台\nname: 应用名称\ndownloadUrl: 应用安装包的下载地址","pushy-deleteapp-appid#pushy deleteApp [appId]":"删除已有应用。所有已创建的应用包、热更新版本都会被同时删除。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-apps#pushy apps":"查看当前已创建的全部应用。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-selectapp-appid#pushy selectApp [appId]":"绑定应用到当前工程。\nplatform: ios|android 对应的平台","pushy-uploadipa-ipafile#pushy uploadIpa [ipaFile]":"上传 ipa 文件到开放平台。\nnote: 备注(cli 需 1.24.0 或更高版本)","pushy-uploadapk-apkfile#pushy uploadApk [apkFile]":"上传 apk 文件到开放平台。\nnote: 备注(cli 需 1.24.0 或更高版本)","pushy-packages#pushy packages":"查看已经上传的原生包。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-publish-ppkfile#pushy publish [ppkFile]":"发布新的热更新版本(ppk 文件)。\nplatform: ios|android 对应的平台\nname: 当前热更新版本的名字(版本号)\ndescription: 当前热更新版本的描述信息,可以对用户进行展示\nmetaInfo: 当前热更新版本的元信息,可以用来保存一些额外信息,具体用法可参考场景实践。","pushy-versions#pushy versions":"分页列举可用的版本。这项操作也可以在网页管理端进行。\nplatform: ios|android 对应的平台","pushy-update#pushy update":"为一个原生包版本绑定一个热更新版本。这项操作也可以在网页管理端进行。以下参数中packageId,packageVersion,minPackageVersion和maxPackageVersion四选一即可。\nplatform: ios|android 对应的平台\nversionId: 要绑定的热更新版本 ID\npackageId: 要绑定的原生包 ID (四选一)\npackageVersion: 要绑定的原生包版本名(四选一,需 cli 版本 1.7.2+)\nminPackageVersion: 要绑定的最低原生包版本,大于等于此版本的将逐个绑定(四选一,需 cli 版本 1.27.0+)\nmaxPackageVersion: 要绑定的最高原生包版本,小于等于此版本的将逐个绑定(四选一,需 cli 版本 1.27.0+)"}},"/docs/faq":{"title":"常见问题","data":{"":"如果本页面没能回答您的疑问,您可以去issues 区或 QQ 群 729013783 提问,或给我们发邮件。","业务问题#业务问题":"","热更新究竟能否上架#热更新究竟能否上架?":"您可能听说过各种说法,但大量实践表明,热更新目前能够顺利上架 AppStore 和其他各种应用市场。唯一需要注意的是,在审核期间请不要发布热更新,不要让审核人员看到各种更新相关的提示和弹窗,即可顺利通过。","是否可以在海外使用#是否可以在海外使用?":"可以的,国内外都有高速 CDN 节点。","哪些修改可以热更新哪些不能#哪些修改可以热更新?哪些不能?":"我们把对应用的修改分为两类:\n不可热更新 —— 原生修改,即所有需要编译后才能生效的修改:\n任何在 iOS 或者 Android 目录中的修改、增删。\n任何含有原生代码的第三方组件的更新、修改。\n可以热更新 —— 非原生修改,即所有无需编译,刷新即可生效的修改:\njs 代码修改,包括第三方纯 js 组件的更新、修改。\n可以在 js 代码中 require/import 的资源文件,例如图片。\n需要注意的是,即便资源文件可以热更新,但这些热更新后的资源文件会以file://协议的形式提供访问,某些读取资源文件的第三方可能并不支持file://协议。","我是否可以搭建自己的热更新服务#我是否可以搭建自己的热更新服务?":"你可以单独使用本组件的原生部分(不包括 js 模块)和命令行工具中的bundle、diff、diffFromIpa、diffFromApk四个功能。这些功能都不会使用我们的热更新服务,也无需注册或登录账号。但你可能要编写自己的 js 模块来与不同的热更新服务器通讯。如果您有兴趣搭建私有云服务,可以邮件联系我们。","热更新成功完成但是重启后又回滚了是怎么回事#热更新成功完成,但是重启后又回滚了是怎么回事?":"可以正常更新,但是重启后回滚,一般有两种可能的情况:\n没有正确配置 bundleUrl\n(仅有 v10 以下版本存在此可能性,v10 及更高版本不存在此情况)没有正确调用 markSuccess\n如果你确定上述两个步骤都正确无误,请在issues 区给我们留言反馈。","热更新报错热更新已暂停原因编译时间戳与服务器记录不一致#热更新报错:“热更新已暂停,原因:编译时间戳与服务器记录不一致。”":"当您每次编译产生一个原生包时,其中都会记录一个编译时间戳buildTime(可以使用pushy parseIpa a.ipa或是pushy parseApk a.apk命令来查看)。如果您需要把这个包发给客户并希望使用热更新功能,那么就需要使用 upload 命令来上传到我们的服务器,而服务器端会记录这个包的版本号和编译时间戳以便后续比对记录。举例来说,假如我已经上传了一个 1.0 版本原生包(1A包),但后来由于种种原因我又重新打包了(1B包)但没有上传,这两个包有相同的版本号,但会有不同的编译时间戳。此时有几种可能的情况:\n我忘记上传过1A包,1B包没有实质更改且还没有客户安装。此时应当放弃1B包,只让客户下载安装1A包。\n我忘记上传过1A包,又生成了1B包且已有客户安装。此时1A包客户可以获取热更新,1B包客户无法获取热更新。要使两批客户都能获取热更新,要么想办法通知1B包客户装回1A包,要么重新发布一个高于1.0版本的原生包且上传到 pushy 服务器,并删除1.0版本的记录,此时1A包和1B包的客户都会收到版本过期需要下载新版本的通知。\n我知道上传过1A包,但我有原生方面的修改需要打了一个新的1B包。此时若将1B包上传到 pushy 服务器会提示版本验证错误,因为已经存在有相同的1.0版本。所以需要首先更改原生版本号再次打包,并首先将此新版本上传到服务器,然后分发给客户。这样1.0版本和新版本可以并存且都可以获得热更新。\n请谨记,任何时候生成原生包,请 1)先修改原生版本号,2)并上传到 pushy 服务器,才可以正常获得热更新。\n如果您是高级版、专业版或大客户VIP版客户,也可以考虑在管理后台的应用设置中启用忽略编译时间戳。此选项仅对比版本号,不校验时间戳,可提供更宽容的热更策略,但可能消耗更多流量,适合需要管理多个分发渠道的客户。","付费问题#付费问题":"","我应该选择哪个业务版本呢#我应该选择哪个业务版本呢?":"其实完全无需考虑,先从最高配额的专业版开始体验(新用户注册验证通过后,将自动获得 7 天专业版试用),各个版本的使用流程完全一致!当试用时间到期后,若当前版本的配额不能满足需求,可随时补差价升级到更高配额的版本。升级自动按天数计算差价,绝无额外费用。目前仅有一项额外的付费专用功能,即忽略编译时间戳设置(在管理后台的应用设置中),此选项仅对比版本号,不校验时间戳,可提供更宽容的热更策略,但可能消耗更多流量,适合需要管理多个分发渠道的客户。此功能目前仅支持高级版、专业版和大客户VIP版。","我可以试用付费版本先评估一下效果吗#我可以试用付费版本先评估一下效果吗?":"新用户注册验证通过后,将自动获得 7 天专业版试用。如您需要更长的评估时间,可将注册用户名、邮箱、公司(或个人)名称发送至 hi@charmlot.com ,标题注明“pushy 评估试用延长”,我们会为您酌情延长评估时间。","配额具体如何计算#配额具体如何计算?":"配额主要分为 4 种:\n可创建的应用数量,注意 iOS 和 Android 版本记做不同的应用。\n原生包数量及大小,不同应用分开计算。原生包指通过pushy uploadIpa/uploadApk命令上传到 Pushy 服务器上作为热更新起始版本的完整 apk/ipa 安装包。\n热更包数量及大小,不同应用分开计算。热更包指通过pushy bundle所命令生成的 ppk 文件。注意这不是用户实际下载的更新文件,用户下载的是通过比对生成的增量 diff 文件(比 ppk 文件要小得多)。\n每日总热更查询次数,所有应用累加计算。每次用户打开应用时,会向服务器查询是否有新的热更新。这个查询次数会被计入每日总热更查询次数。当日超过限额次数后,接口将不再返回新的热更新信息,直到次日重置。\n这里有一些对包大小的优化建议可供参考。\n以上 2、3 条为账户下每个应用单独计算,第 4 条为账户下所有应用累加计算。具体配额和费用由于运营成本可能会有调整,请以价格表页面为准。已购买的用户在续费前不受调整影响。","达到配额后还可以正常使用吗#达到配额后还可以正常使用吗?":"当应用、原生包、热更包数量达到配额限制后,您将无法执行相应的新增操作。但用户端仍可正常检查更新,下载已发布的更新。您可选择升级到更高配额,或是通过删除已不再使用的应用、原生包、热更包来使数量降低到配额以内,以便继续执行新增操作。\n若原生包、热更包大小超过限额,则会导致上传失败。您可以选择升级到更高配额,或是想办法进行精简优化。\n若当日热更检查次数超过限额,则会导致接口返回空数据。您可以选择升级到更高配额,或是优化热更策略,减少用户端检查更新的频率。","我可以单独升级某项配额吗#我可以单独升级某项配额吗?":"我们暂时无法为某个单项配额提供升级选项,请选择更高版本的配额。如果专业版配额仍然不能满足需求,请通过邮件 hi@charmlot.com 与我们联系,我们将根据您的个性化需求为您定制方案。","付费业务到期后不续费现有应用还能否正常使用#付费业务到期后不续费,现有应用还能否正常使用?":"到期后按免费版额度执行。如您现有应用已超出配额,则必须通过删除操作降低到配额以内,方可执行新的操作(如创建应用,上传热更等),但不影响用户获取之前已发布的热更。","如何获取付费的专人技术支持#如何获取付费的专人技术支持?":"请将您的用户名和订单截图发送至 hi@charmlot.com ,同时提供您希望的技术联系方式(QQ、微信等),核实后会有技术专员添加您为好友。","如何开具发票#如何开具发票?":"请将具体开票需求发送至 hi@charmlot.com ,并附上注册邮箱和订单截图。我们默认会回复普通电子发票到注册邮箱,类目为软件服务。如需指定接受邮箱或手机,请在邮件正文中注明。如需要邮寄纸质发票请注明邮寄地址,邮费为到付。\n如邮件发送后三个工作日内未收到回复,请联系 QQ 客服 34731408。","可以使用银行转账付款吗#可以使用银行转账付款吗?":"可以的。请对照价格表,将对应版本的款项转账至:\n公司名称\t武汉青罗网络科技有限公司\t开户行名称\t浙江网商银行\t开户地区\t浙江省杭州市\t支行名称\t浙江网商银行股份有限公司\t账号\t8888888048825564\t\n转账完成后请截图发送至 hi@charmlot.com ,并写明注册邮箱,我们将在一个工作日内开通对应服务。","可以按月付款吗#可以按月付款吗?":"需要月付的客户请联系 QQ 客服 34731408,月付价格为年付价格 / 8。如标准版当前价格为 800 元每年,则月付价格为 100 元每月。\n月付客户需累计支付满 500 元后才能开具发票。大客户VIP版不支持月付。","大客户方案#大客户方案":"如果您的应用有大规模的活跃用户,日均所有应用累计总查询次数超过 100 万次,则建议选择我们专门针对大规模应用优化的大客户VIP版。\n大客户VIP版在应用数量、原生包数量、热更包数量等方面配额与专业版相同。当前价格如下表:\n服务版本\t日均总热更查询次数\t价格(元/年)\tVIP1\t100 万至 1000 万次\t30000\tVIP2\t1000 万至 5000 万次\t60000\tVIP3\t5000 万至 1 亿次\t120000\tVIP4\t1 亿次以上\t请联系客服\t\n具体试用和对接请联系 QQ 客服 34731408"}},"/docs/getting-started":{"title":"安装配置","data":{"":"请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里\n首先你应该有一个基于 React Native 开发的应用,我们把具有 package.json 的目录叫做你的应用根目录。\n如果你还没有初始化应用,请参阅开始使用 React Native。我们假设你已经拥有了开发 React Native 应用的一切环境,包括Node.js、Xcode、Android SDK等等。","安装#安装":"在你的项目根目录下运行以下命令:\n# 先全局安装命令行工具,每台电脑只用装一次\nnpm i -g react-native-update-cli\n# 然后在项目目录中安装热更新模块\nnpm i react-native-update\n# 然后进入ios目录安装iOS模块\ncd ios && pod install\n如果下载极慢或者显示网络失败,请设置使用淘宝镜像 npx nrm use taobao\n注意请不要混用npm/yarn/pnpm等包管理器及对应的lock文件,团队成员请坚持使用同一包管理器,且仅保留统一格式的lock文件\n请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(使用 npx\nreact-native run-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。","手动-link#手动 link":"如果 RN 版本 >= 0.60 则不需要此手动 link 步骤。\n注意:如果是混编 RN\n项目,或monorepo,或任何其他自定义的情况,由于自定义的配置可能不完整或不适应RN的目录结构,导致自动\nlink 的功能可能不能正常工作。此时即便 RN 版本 >= 0.60,你可能也需要执行手动\nlink 操作。","ios#iOS":"RN < 0.60且使用CocoaPods(推荐)\n在 ios/Podfile 中添加\npod 'react-native-update', path: '../node_modules/react-native-update'\n在项目的 ios 目录下运行pod install\n重新编译\nRN < 0.60且不使用CocoaPods\n在 XCode 中的 Project Navigator 里,右键点击Libraries ➜ Add Files to [你的工程名]\n进入node_modules ➜ react-native-update ➜ ios 并选中RCTPushy.xcodeproj`\n在 XCode 中的 project navigator 里,选中你的工程,在 Build Phases ➜ Link Binary With Libraries 中添加 libRCTPushy.a、libz.tbd、libbz2.1.0.tbd\n继续在Build Settings里搜索Header Search Path,添加$(SRCROOT)/../node_modules/react-native-update/ios,勾选recursive。\n在Build Phases添加一个New Run Script Phase运行脚本,内容如下\n#!/bin/bash\nset -x\nDEST=\"../node_modules/react-native-update/ios/\"\ndate +%s > \"$DEST/pushy_build_time.txt\"\n尝试编译一下,顺利的话就会在../node_modules/react-native-update/ios/文件夹下面生成一个pushy_build_time.txt文件。然后在Copy Bundle Resources里把生成的pushy_build_time.txt文件添加进去。","android#Android":"RN < 0.60 或其他不能自动 link 的情况\n在android/settings.gradle中添加如下代码:\ninclude ':react-native-update'\nproject(':react-native-update').projectDir = new File(rootProject.projectDir, \t'../node_modules/react-native-update/android')\n在android/app/build.gradle的 dependencies 部分增加如下代码:\nimplementation project(':react-native-update')\n打开android/app/src/main/java/[...]/MainApplication.java,\n在文件开头增加 import cn.reactnative.modules.update.UpdatePackage;\n在getPackages() 方法中增加 new UpdatePackage()(注意上一行可能要增加一个逗号)","配置-bundle-url#配置 Bundle URL":"注意此步骤无论任何版本,目前都需要手动配置。","ios-1#iOS":"在你的 AppDelegate.mm 或 AppDelegate.m 文件(不同 RN 版本可能后缀名不同)中增加如下代码:\n// ... 其它代码\n#import \"AppDelegate.h\"\n#import \"RCTPushy.h\" // <-- import头文件,注意要放到if条件外面\n// 可能项目里有一些条件编译语句,例如有些版本RN自带的flipper\n// #if DEBUG\n// 注意**不要**在这里面引入\"RCTPushy.h\"\n// #import \n// ...\n// #endif\n// rn 版本 >= 0.74 需要修改 bundleURL 方法\n- (NSURL *)bundleURL\n{\n#if DEBUG\n // 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新)\n return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@\"index\"];\n#else\n return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle\n#endif\n}\n// rn 版本 < 0.74 需要修改sourceURLForBridge方法\n- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge\n{\n#if DEBUG\n // 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新)\n return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@\"index\"];\n#else\n return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle\n#endif\n}","android-1#Android":"在 MainApplication 中增加如下代码(如果是混编原生的项目或其他原因没有使用 ReactApplication,请使用此 api 集成):\n// ... 其它代码\n// ↓↓↓请注意不要少了这句import\nimport cn.reactnative.modules.update.UpdateContext;\n// ↑↑↑\nclass MainApplication : Application(), ReactApplication {\n override val reactNativeHost: ReactNativeHost =\n object : DefaultReactNativeHost(this) {\n // ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!\n override fun getJSBundleFile(): String? {\n return UpdateContext.getBundleUrl(this@MainApplication)\n }\n // ↑↑↑\n // ...其他代码\n }\n}\n// ... 其它代码\n// ↓↓↓请注意不要少了这句import\nimport cn.reactnative.modules.update.UpdateContext;\n// ↑↑↑\npublic class MainApplication extends Application implements ReactApplication {\n private final ReactNativeHost mReactNativeHost =\n // 老版本 RN 这里可能是 new ReactNativeHost(this)\n new DefaultReactNativeHost(this) {\n // ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!\n @Override\n protected String getJSBundleFile() {\n return UpdateContext.getBundleUrl(MainApplication.this);\n }\n // ↑↑↑\n // ...其他代码\n }\n}\n请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(npx react-native\nrun-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。","禁用-android-的-crunch-优化#禁用 android 的 crunch 优化":"android 会在生成 apk 时自动对 png 图片进行压缩,此操作既耗时又影响增量补丁的生成。为了保证补丁能正常生成,您需要在android/app/build.gradle中关闭此操作:\n...\nandroid {\n ...\n signingConfigs { ... }\n buildTypes {\n release {\n ...\n // 添加下面这行以禁用crunch\n crunchPngs false\n }\n }\n}\n...","登录与创建应用#登录与创建应用":"首先请在 https://pushy-admin.reactnative.cn 注册帐号,然后在你的项目根目录下运行以下命令:\n$ pushy login\nemail: <输入你的注册邮箱>\npassword: <输入你的密码>\n这会在项目文件夹下创建一个.update文件,注意不要把这个文件上传到 Git 等 CVS 系统上。你可以在.gitignore末尾增加一行.update来忽略这个文件。登录之后可以创建应用。注意 iOS 平台和安卓平台需要分别创建:\n$ pushy createApp --platform ios\nApp Name: <输入应用名字>\n$ pushy createApp --platform android\nApp Name: <输入应用名字>\n两次输入的名字可以相同,这没有关系。\n如果你已经在网页端或者其它地方创建过应用,也可以直接选择应用:\n$ pushy selectApp --platform ios\n1) 鱼多多(ios)\n2) 招财旺(ios)\nTotal 2 ios apps\nEnter appId: <输入应用前面的编号>\n选择或者创建过应用后,你将可以在文件夹下看到update.json文件,其内容类似如下形式:\n{\n \"ios\": {\n \"appId\": 1,\n \"appKey\": \"<一串随机字符串>\"\n },\n \"android\": {\n \"appId\": 2,\n \"appKey\": \"<一串随机字符串>\"\n }\n}\n你可以安全的把update.json上传到 Git 等 CVS 系统上,与你的团队共享这个文件,它不包含任何敏感信息。当然,他们在使用任何功能之前,都必须首先输入pushy login进行登录。至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅代码集成。"}},"/docs/integration":{"title":"代码集成","data":{"":"请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里\n安装配置完成后,确定应用编译顺利通过,下面我们来进行代码集成。","获取-appkey#获取 appKey":"检查更新时必须提供你的appKey,这个值保存在update.json中(使用pushy createApp或pushy selectApp命令后会自动生成),并且根据平台不同而不同。你可以用如下的代码获取appKey:\nimport { Platform } from \"react-native\";\nimport _updateConfig from \"./update.json\";\nconst { appKey } = _updateConfig[Platform.OS];\n如果你不使用 pushy 命令行,也可以从网页端查看到两个应用 appKey,并根据平台的不同来选择。","初始化服务#初始化服务":"import { PushyProvider, Pushy } from \"react-native-update\";\n// 唯一必填参数是appKey,其他选项请参阅 api 文档\nconst pushyClient = new Pushy({\n appKey,\n // 注意,默认情况下,在开发环境中不会检查更新\n // 如需在开发环境中调试更新,请设置debug为true\n // 但即便打开此选项,也仅能检查、下载热更,并不能实际应用热更。实际应用热更必须在release包中进行。\n // debug: true\n});\n// 在根组件外加上PushyProvider后导出\nexport default function Root() {\n // 注意,在使用 PushyProvider 的当前组件中,无法直接调用 usePushy\n // 只有当前组件的子组件才能调用 usePushy\n return (\n \n {/* ↓ 整个应用的根组件放到PushyProvider中 */}\n \n \n );\n}\n如没有特别的自定义需求,那么到此热更新已经可以开始正常运作(如需在应用内执行 apk 更新,还需配置安装权限)。默认配置下,在 App 启动,以及从后台切换到前台时会触发更新检查,弹出提示的内容也固定。如需自定义触发时机,以及修改界面提示等,请参考下面的自定义更新界面。","自定义更新界面#自定义更新界面":"默认配置下,pushy 会以系统 alert 的形式来弹出更新提示,如需自定义更新界面,首先请关闭默认的 updateStrategy 更新策略,并打开 debug 选项以便调试:\nconst pushyClient = new Pushy({\n appKey,\n+ updateStrategy: null,\n+ debug: true,\n});\n所有更新相关的数据可以通过一个单一的usePushy()hook 函数来获取,然后可以根据其提供的数据来自行渲染自定义的界面,如下面的例子:\nimport { Icon, PaperProvider, Snackbar, Banner } from \"react-native-paper\";\nfunction App() {\n const {\n client,\n checkUpdate,\n downloadUpdate,\n switchVersionLater,\n switchVersion,\n updateInfo,\n packageVersion,\n currentHash,\n progress: { received, total } = {},\n } = usePushy();\n const [showUpdateBanner, setShowUpdateBanner] = useState(false);\n const [showUpdateSnackbar, setShowUpdateSnackbar] = useState(false);\n const snackbarVisible =\n showUpdateSnackbar && updateInfo?.update;\n return (\n \n \n 更新下载进度:{received} / {total}\n \n \n 点击这里检查更新\n \n {snackbarVisible && (\n {\n setShowUpdateSnackbar(false);\n }}\n action={{\n label: \"更新\",\n onPress: async () => {\n setShowUpdateSnackbar(false);\n await downloadUpdate();\n setShowUpdateBanner(true);\n },\n }}\n >\n 有新版本({updateInfo.name})可用,是否更新?\n \n )}\n {\n switchVersionLater();\n setShowUpdateBanner(false);\n },\n },\n ]}\n icon={({ size }) => (\n \n )}\n >\n 更新已完成,是否立即重启?\n \n \n );\n}\n其中checkUpdate方法可以用来手动触发更新检查。检查后会更新usePushy返回的updateInfo(注意checkUpdate方法本身没有返回值),有三种情况:\n{expired: true}:该应用原生包已过期(三种情况:1. 主动设置为过期状态,2. 主动删除,3. 从未上传),开发者应该在 pushy 的管理后台添加一个更新下载链接,并自行提示用户下载。如需在应用内执行 apk 更新,还需配置安装权限。\n{upToDate: true}:当前已经更新到最新,无需进行更新。\n{update: true}:当前有新版本可以更新。info 的name、description字段可以用于提示用户,而metaInfo字段则可以根据你的需求自定义其它属性(如是否静默更新、是否强制更新等等),具体用法可参考场景实践。另外还有几个字段,包含了补丁包的下载地址等。 pushy 会首先尝试耗费流量更少的更新方式。\n当返回的updateInfo中update字段为 true 时,即可调用downloadUpdate方法来下载更新,此时可以获取到下载的进度数据progress。下载完成后可以调用switchVersion来立即重启更新,也可以使用switchVersionLater来标记下次启动时更新。","统计数据#统计数据":"初始化 Pushy 客户端时可以传入自定义的 logger 函数,其中可以自己记录日志或上报统计数据,比如下面的例子使用 Google Analytics 来上报事件:\nimport { getAnalytics, logEvent } from \"firebase/analytics\";\nconst analytics = getAnalytics();\nconst pushyClient = new Pushy({\n appKey,\n logger: ({ type, data }) => {\n logEvent(analytics, \"pushy_\" + type, data);\n },\n});\n以上提及的所有 api 的说明文档可在这里查看。现在,你的应用已经可以通过 pushy 服务检查版本并进行更新了。下一步,你可以开始尝试发布应用包和版本,请参阅发布热更新。"}},"/docs/publish":{"title":"发布热更新","data":{"":"现在你的应用已经具备了检测更新的功能,下面我们来尝试发布并更新它。流程可参考下图:\n流程总结如下:\n我们需要先打包一个原生 release 版本,在打包前请确保已集成了react-native-update并在调试过程中运行正常,安卓端关闭了crunchPngs设置,打包说明可参考iOS 打包和android 打包。打包完成后请使用pushy uploadIpa或者pushy uploadApk命令来把这个安装包上传到 pushy 服务器端,以作为之后热更差量对比的基准。同时请保留好这个安装包,上架和分发给用户所使用的安装包需要和服务器端完全一致。建议使用 git tag 功能来标记原生版本号(例如v1.0.0)。\n然后在基准版本之上迭代业务逻辑(增删 js 代码,增删图片等静态资源),使用pushy bundle命令来生成和发布热更新版本,而不需要重新打包。建议使用 git tag 功能来标记热更版本号(例如v1.0.1)。\n如果迭代过程中有原生方面的修改,则需要发布并上传新的原生基准版本(重复步骤 1,但需要设置不同的原生版本号)。可以只保留一个原生基准版本,也可以多版本同时维护。","发布原生基准版本#发布原生基准版本":"","ios#iOS":"首先参考文档-在设备上运行,确定你正在使用离线包。然后点击菜单。按照正常的发布流程打包.ipa文件:\nXcode 中运行设备选真机或 Generic iOS Device\n菜单中选择 Product - Archive\nArchive 完成后选择Export生成.ipa 文件\n然后运行如下命令上传到 pushy 服务器以供后续版本比对之用\n$ pushy uploadIpa \n此 ipa 的CFBundleShortVersionString字段(位于ios/项目名/Info.plist中)会被记录为原生版本号packageVersion。随后你可以选择往 AppStore 上传这个版本(可以重新 export 并调整相关选项,但请不要重新 archive),也可以先通过Test flight或蒲公英等渠道进行真机安装测试。请注意:暂不支持通过 Xcode 直接进行热更新测试。如果后续需要再次 archive 打包(例如修改原生代码或配置。如果只是修改 js 代码则不需要重新打包。),请先更改版本号,并在打包完成后再次uploadIpa到服务器端记录,否则后续生成的相同版本的原生包会由于编译时间戳不一致而无法获取热更新。\n注意:如果你在上传之前就运行了新的原生版本,由于服务器端没有记录,会暂停其更新数小时。可删除原先安装的 app 再重新安装以清空暂停设置。在上传之后安装的客户端不会受此影响。","android#Android":"首先参考文档-打包 APK设置签名,然后在 android 文件夹下运行./gradlew assembleRelease或./gradlew aR,你就可以在android/app/build/outputs/apk/release/app-release.apk中找到你的应用包。\n如果你需要使用 aab 格式(android app bundle,google 市场专用)的包,请参考这里的做法将其转换为 apk 格式后再操作。\n然后运行如下命令\n$ pushy uploadApk android/app/build/outputs/apk/release/app-release.apk\n即可上传 apk 以供后续版本比对之用。此 apk 的versionName字段(位于android/app/build.gralde中)会被记录为原生版本号packageVersion。随后你可以选择往应用市场发布这个版本,也可以先往设备上直接安装这个 apk 文件以进行测试。如果后续需要再次打包(例如修改原生代码或配置。如果只是修改 js 代码则不需要重新打包。),请先更改版本号,并再次uploadApk到服务器端记录,否则后续生成的相同版本的原生包会由于编译时间戳不一致而无法获取热更新。\n注意:如果你在上传之前就运行了新的原生版本,由于服务器端没有记录,会暂停其更新数小时。可删除原先安装的 app 再重新安装以清空暂停设置。在上传之后安装的客户端不会受此影响。","发布热更新版本#发布热更新版本":"你可以尝试修改一行代码(譬如将版本一修改为版本二),然后使用pushy bundle --platform 命令来生成新的热更新版本。\n$ pushy bundle --platform android\nBundling with React Native version: 0.22.2\n<各种进度输出>\nBundled saved to: build/output/android.1459850548545.ppk\nWould you like to publish it?(Y/N)\n如果想要立即上传,此时输入 Y。当然,你也可以在将来使用pushy publish --platform android build/output/android.1459850548545.ppk来上传刚才打包好的热更新包。\n Uploading [========================================================] 100% 0.0s\nEnter version name: <输入热更新版本名字,如1.0.0-rc>\nEnter description: <输入热更新版本描述>\nEnter meta info: {\"ok\":1}\nOk.\nWould you like to bind packages to this version?(Y/N)\n此时版本已经提交到 pushy 服务,但用户暂时看不到此更新,你需要先将特定的原生包版本绑定到此热更新版本上。此时输入 Y 立即绑定,你也可以在将来使用pushy update --platform 来对已上传的热更包和原生包进行绑定。除此以外,你还可以在网页端操作,简单的将对应的原生包版本拖到需要的热更新版本下即可。\n┌────────────┬──────────────────────────────────────┐\n│ Package Id │ Version │\n├────────────┼──────────────────────────────────────┤\n│ 46272 │ 2.0(normal) │\n├────────────┼──────────────────────────────────────┤\n│ 45577 │ 1.0(normal) │\n└────────────┴──────────────────────────────────────┘\n共 2 个包\n输入原生包 id: 46272\n版本绑定完毕后,服务器会在几秒内生成差量补丁,客户端就可以获取到更新了。后续要继续发布新的热更新,只需反复执行pushy bundle命令即可,不需要重新打包。恭喜你,至此为止,你已经完成了植入代码热更新的全部工作。","测试发布与回滚#测试、发布与回滚":"我们强烈建议您先发布一个测试包,再发布一个除了版本号以外均完全相同的正式包。例如,假设我们有一个正式包,版本为1.6.0,那么可以修改版本号重新打包一个1001.6.0,以一个明显不太正常的版本号来标识它是一个测试版本,同时后几位相同,可以表明它和某个正式版本存在关联(内容/依赖一致)。在每次往发布包发起热更新之前,先对测试包1001.6.0进行更新操作,基本测试通过之后,再在网页后台上将热更包重新绑定到正式包1.6.0上。如果在测试包中发现了重大问题,你就可以先进行修复,更新测试确认通过后再部署到正式线上环境。这样,可以最大程度的避免发生线上事故。万一确实发生线上事故需要回滚的话,首先利用版本控制系统回滚代码到正常的状态,然后重新生成热更包并推送即可。"}},"/":{"title":"Index","data":{}},"/pricing":{"title":"价格","data":{}}} \ No newline at end of file diff --git a/_next/static/chunks/pages/docs/getting-started-71e1575098153bf8.js b/_next/static/chunks/pages/docs/getting-started-71e1575098153bf8.js deleted file mode 100644 index df4a4eb..0000000 --- a/_next/static/chunks/pages/docs/getting-started-71e1575098153bf8.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[153],{28675:function(s,e,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/docs/getting-started",function(){return n(48902)}])},48902:function(s,e,n){"use strict";n.r(e),n.d(e,{__toc:function(){return a}});var l=n(52676),i=n(31781),r=n(53743),o=n(58689);let a=[{depth:3,value:"安装",id:"安装"},{depth:3,value:"手动 link",id:"手动-link"},{depth:4,value:"iOS",id:"ios"},{depth:4,value:"Android",id:"android"},{depth:3,value:"配置 Bundle URL",id:"配置-bundle-url"},{depth:4,value:"iOS",id:"ios-1"},{depth:4,value:"Android",id:"android-1"},{depth:3,value:"禁用 android 的 crunch 优化",id:"禁用-android-的-crunch-优化"},{depth:3,value:"登录与创建应用",id:"登录与创建应用"}];function c(s){let e=Object.assign({p:"p",a:"a",code:"code",h3:"h3",pre:"pre",span:"span",h4:"h4",details:"details",summary:"summary",ol:"ol",li:"li",ul:"ul"},(0,r.a)(),s.components);return o.mQ||t("Tabs",!1),o.mQ.Tab||t("Tabs.Tab",!0),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsxs)(e.p,{children:["请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击",(0,l.jsx)(e.a,{href:"https://v9--pushy-site.netlify.app/",children:"这里"})]})}),"\n",(0,l.jsxs)(e.p,{children:["首先你应该有一个基于 React Native 开发的应用,我们把具有 package.json 的目录叫做你的",(0,l.jsx)(e.code,{children:"应用根目录"}),"。\n如果你还没有初始化应用,请参阅",(0,l.jsx)(e.a,{href:"https://reactnative.cn/docs/getting-started",children:"开始使用 React Native"}),"。"]}),"\n",(0,l.jsxs)(e.p,{children:["我们假设你已经拥有了开发 React Native 应用的一切环境,包括",(0,l.jsx)(e.code,{children:"Node.js"}),"、",(0,l.jsx)(e.code,{children:"Xcode"}),"、",(0,l.jsx)(e.code,{children:"Android SDK"}),"等等。"]}),"\n",(0,l.jsxs)(o.Rg,{children:[(0,l.jsx)(e.h3,{id:"安装",children:"安装"}),(0,l.jsx)(e.p,{children:"在你的项目根目录下运行以下命令:"}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"# 先全局安装命令行工具,每台电脑只用装一次"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"i"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"-g"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"react-native-update-cli"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"# 然后在项目目录中安装热更新模块"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"i"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"react-native-update"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"# 然后进入ios目录安装iOS模块"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"&&"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"pod"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"install"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "})]})}),(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsxs)(e.p,{children:["如果下载极慢或者显示网络失败,请设置使用淘宝镜像 ",(0,l.jsx)(e.code,{children:"npx nrm use taobao"})]})}),(0,l.jsx)(o.UW,{type:"warning",children:(0,l.jsxs)(e.p,{children:["注意请不要混用",(0,l.jsx)(e.code,{children:"npm/yarn/pnpm"}),"等包管理器及对应的",(0,l.jsx)(e.code,{children:"lock"}),"文件,团队成员请坚持使用同一包管理器,且仅保留统一格式的",(0,l.jsx)(e.code,{children:"lock"}),"文件"]})}),(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsx)(e.p,{children:"请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(使用 npx\nreact-native run-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。"})}),(0,l.jsx)(e.h3,{id:"手动-link",children:"手动 link"}),(0,l.jsx)(e.p,{children:"如果 RN 版本 >= 0.60 则不需要此手动 link 步骤。"}),(0,l.jsx)(o.UW,{type:"warning",children:(0,l.jsx)(e.p,{children:"注意:如果是混编 RN\n项目,或monorepo,或任何其他自定义的情况,由于自定义的配置可能不完整或不适应RN的目录结构,导致自动\nlink 的功能可能不能正常工作。此时即便 RN 版本 >= 0.60,你可能也需要执行手动\nlink 操作。"})}),(0,l.jsx)(e.h4,{id:"ios",children:"iOS"}),(0,l.jsxs)(e.details,{children:[(0,l.jsx)(e.summary,{children:"RN < 0.60且使用CocoaPods(推荐)"}),(0,l.jsxs)(e.ol,{children:["\n",(0,l.jsx)(e.li,{children:"在 ios/Podfile 中添加"}),"\n"]}),(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.code,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"pod 'react-native-update', path: '../node_modules/react-native-update'"})})})}),(0,l.jsxs)(e.ol,{start:"2",children:["\n",(0,l.jsxs)(e.li,{children:["在项目的 ios 目录下运行",(0,l.jsx)(e.code,{children:"pod install"})]}),"\n",(0,l.jsx)(e.li,{children:"重新编译"}),"\n"]})]}),(0,l.jsxs)(e.details,{children:[(0,l.jsx)(e.summary,{children:"RN < 0.60且不使用CocoaPods"}),(0,l.jsxs)(e.ol,{children:["\n",(0,l.jsxs)(e.li,{children:["在 XCode 中的 Project Navigator 里,右键点击",(0,l.jsx)(e.code,{children:"Libraries"})," ➜ ",(0,l.jsx)(e.code,{children:"Add Files to [你的工程名]"})]}),"\n",(0,l.jsxs)(e.li,{children:["进入",(0,l.jsx)(e.code,{children:"node_modules"})," ➜ ",(0,l.jsx)(e.code,{children:"react-native-update"})," ➜ ",(0,l.jsx)(e.code,{children:"ios 并选中"}),"RCTPushy.xcodeproj`"]}),"\n",(0,l.jsxs)(e.li,{children:["在 XCode 中的 project navigator 里,选中你的工程,在 ",(0,l.jsx)(e.code,{children:"Build Phases"})," ➜ ",(0,l.jsx)(e.code,{children:"Link Binary With Libraries"})," 中添加 ",(0,l.jsx)(e.code,{children:"libRCTPushy.a"}),"、",(0,l.jsx)(e.code,{children:"libz.tbd"}),"、",(0,l.jsx)(e.code,{children:"libbz2.1.0.tbd"})]}),"\n",(0,l.jsxs)(e.li,{children:["继续在",(0,l.jsx)(e.code,{children:"Build Settings"}),"里搜索",(0,l.jsx)(e.code,{children:"Header Search Path"}),",添加",(0,l.jsx)(e.code,{children:"$(SRCROOT)/../node_modules/react-native-update/ios"}),",勾选",(0,l.jsx)(e.code,{children:"recursive"}),"。"]}),"\n",(0,l.jsxs)(e.li,{children:["在",(0,l.jsx)(e.code,{children:"Build Phases"}),"添加一个",(0,l.jsx)(e.code,{children:"New Run Script Phase"}),"运行脚本,内容如下"]}),"\n"]}),(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"text","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"#!/bin/bash"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"set -x"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:'DEST="../node_modules/react-native-update/ios/"'})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:'date +%s > "$DEST/pushy_build_time.txt"'})})]})}),(0,l.jsxs)(e.ol,{start:"7",children:["\n",(0,l.jsxs)(e.li,{children:["尝试编译一下,顺利的话就会在",(0,l.jsx)(e.code,{children:"../node_modules/react-native-update/ios/"}),"文件夹下面生成一个",(0,l.jsx)(e.code,{children:"pushy_build_time.txt"}),"文件。然后在",(0,l.jsx)(e.code,{children:"Copy Bundle Resources"}),"里把生成的",(0,l.jsx)(e.code,{children:"pushy_build_time.txt"}),"文件添加进去。"]}),"\n"]})]}),(0,l.jsx)(e.h4,{id:"android",children:"Android"}),(0,l.jsxs)(e.details,{children:[(0,l.jsx)(e.summary,{children:"RN < 0.60 或其他不能自动 link 的情况"}),(0,l.jsxs)(e.ol,{children:["\n",(0,l.jsxs)(e.li,{children:["\n",(0,l.jsxs)(e.p,{children:["在",(0,l.jsx)(e.code,{children:"android/settings.gradle"}),"中添加如下代码:"]}),"\n",(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"text","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"include ':react-native-update'"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"project(':react-native-update').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-update/android')"})})]})}),"\n"]}),"\n",(0,l.jsxs)(e.li,{children:["\n",(0,l.jsxs)(e.p,{children:["在",(0,l.jsx)(e.code,{children:"android/app/build.gradle"}),"的 dependencies 部分增加如下代码:"]}),"\n",(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.code,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"implementation project(':react-native-update')"})})})}),"\n"]}),"\n",(0,l.jsxs)(e.li,{children:["\n",(0,l.jsxs)(e.p,{children:["打开",(0,l.jsx)(e.code,{children:"android/app/src/main/java/[...]/MainApplication.java"}),","]}),"\n"]}),"\n"]}),(0,l.jsxs)(e.ul,{children:["\n",(0,l.jsxs)(e.li,{children:["在文件开头增加 ",(0,l.jsx)(e.code,{children:"import cn.reactnative.modules.update.UpdatePackage;"})]}),"\n",(0,l.jsxs)(e.li,{children:["在",(0,l.jsx)(e.code,{children:"getPackages()"})," 方法中增加 ",(0,l.jsx)(e.code,{children:"new UpdatePackage()"}),"(注意上一行可能要增加一个逗号)"]}),"\n"]})]}),(0,l.jsx)(e.h3,{id:"配置-bundle-url",children:"配置 Bundle URL"}),(0,l.jsx)(e.p,{children:"注意此步骤无论任何版本,目前都需要手动配置。"}),(0,l.jsx)(e.h4,{id:"ios-1",children:"iOS"}),(0,l.jsx)(e.p,{children:"在你的 AppDelegate.mm 或 AppDelegate.m 文件(不同 RN 版本可能后缀名不同)中增加如下代码:"}),(0,l.jsx)(e.pre,{"data-language":"c","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"c","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ... 其它代码"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"AppDelegate.h"'})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"RCTPushy.h"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// <-- import头文件,注意要放到if条件外面"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 可能项目里有一些条件编译语句,例如有些版本RN自带的flipper"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// #if DEBUG"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:'// 注意**不要**在这里面引入"RCTPushy.h"'})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// #import "})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// #endif"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 修改sourceURLForBridge方法"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"-"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" (NSURL "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")sourceURLForBridge:(RCTBridge "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")bridge"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#if"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"DEBUG"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新)"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"index"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"];"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#else"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" [RCTPushy bundleURL];"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <-- 把这里非DEBUG的情况替换为热更新bundle"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#endif"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "})]})}),(0,l.jsx)(e.h4,{id:"android-1",children:"Android"}),(0,l.jsxs)(e.p,{children:["在 MainApplication 中增加如下代码(如果是混编原生的项目或其他原因没有使用 ReactApplication,请",(0,l.jsx)(e.a,{href:"api#updatecontextsetcustominstancemanagerreactinstancemanager-instancemanager",children:"使用此 api 集成"}),"):"]}),(0,l.jsxs)(o.mQ,{items:["Kotlin","Java"],children:[(0,l.jsx)(o.mQ.Tab,{children:(0,l.jsx)(e.pre,{"data-language":"kotlin","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"kotlin","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ... 其它代码"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓请注意不要少了这句import"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" cn.reactnative.modules.update.UpdateContext;"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"class"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" : "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Application"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"(), "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ReactApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"override"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"val"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" reactNativeHost: "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ReactNativeHost"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" object : "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"DefaultReactNativeHost"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"this"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:") {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"override"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"fun"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getJSBundleFile"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"(): "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"String"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"? {"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" UpdateContext."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getBundleUrl"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this@MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ...其他代码"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})})]})})}),(0,l.jsx)(o.mQ.Tab,{children:(0,l.jsx)(e.pre,{"data-language":"java","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"java","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ... 其它代码"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓请注意不要少了这句import"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"cn"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"reactnative"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"modules"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"update"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"UpdateContext"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:";"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"public"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"class"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"extends"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Application"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"implements"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ReactApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"private"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"final"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"ReactNativeHost"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" mReactNativeHost "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 老版本 RN 这里可能是 new ReactNativeHost(this)"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"new"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"DefaultReactNativeHost("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:")"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" @"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"Override"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"protected"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"String"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getJSBundleFile"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"() {"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"UpdateContext"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getBundleUrl"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ...其他代码"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})})]})})})]}),(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsx)(e.p,{children:"请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(npx react-native\nrun-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。"})}),(0,l.jsx)(e.h3,{id:"禁用-android-的-crunch-优化",children:"禁用 android 的 crunch 优化"}),(0,l.jsxs)(e.p,{children:["android 会在生成 apk 时自动对 png 图片进行压缩,此操作既耗时又影响增量补丁的生成。为了保证补丁能正常生成,您需要在",(0,l.jsx)(e.code,{children:"android/app/build.gradle"}),"中关闭此操作:"]}),(0,l.jsx)(e.pre,{"data-language":"gradle","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"gradle","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"android {"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" ..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" signingConfigs { ... }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" buildTypes {"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" release {"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" ..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" // 添加下面这行以禁用crunch"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" crunchPngs false"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"..."})})]})}),(0,l.jsx)(e.h3,{id:"登录与创建应用",children:"登录与创建应用"}),(0,l.jsxs)(e.p,{children:["首先请在 ",(0,l.jsx)(e.a,{href:"https://pushy-admin.reactnative.cn",children:"https://pushy-admin.reactnative.cn"})," 注册帐号,然后在你的项目根目录下运行以下命令:"]}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"login"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"email:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入你的注册邮"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"箱"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"password:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入你的密"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"码"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]})]})}),(0,l.jsxs)(e.p,{children:["这会在项目文件夹下创建一个",(0,l.jsx)(e.code,{children:".update"}),"文件,注意不要把这个文件上传到 Git 等 CVS 系统上。你可以在",(0,l.jsx)(e.code,{children:".gitignore"}),"末尾增加一行",(0,l.jsx)(e.code,{children:".update"}),"来忽略这个文件。"]}),(0,l.jsx)(e.p,{children:"登录之后可以创建应用。注意 iOS 平台和安卓平台需要分别创建:"}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"createApp"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"--platform"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"App"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"Name:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入应用名"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"字"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"createApp"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"--platform"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"android"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"App"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"Name:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入应用名"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"字"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]})]})}),(0,l.jsx)(o.UW,{type:"info",children:"两次输入的名字可以相同,这没有关系。"}),(0,l.jsx)(e.p,{children:"如果你已经在网页端或者其它地方创建过应用,也可以直接选择应用:"}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"selectApp"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"--platform"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"1"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:") 鱼多多("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"2"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:") 招财旺("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Total"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"2"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"apps"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Enter"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"appId:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入应用前面的编"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"号"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]})]})}),(0,l.jsxs)(e.p,{children:["选择或者创建过应用后,你将可以在文件夹下看到",(0,l.jsx)(e.code,{children:"update.json"}),"文件,其内容类似如下形式:"]}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"ios"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"{"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appId"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"1"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:","})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appKey"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"<一串随机字符串>"'})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" },"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"android"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"{"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appId"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"2"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:","})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appKey"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"<一串随机字符串>"'})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})})]})}),(0,l.jsxs)(e.p,{children:["你可以安全的把",(0,l.jsx)(e.code,{children:"update.json"}),"上传到 Git 等 CVS 系统上,与你的团队共享这个文件,它不包含任何敏感信息。当然,他们在使用任何功能之前,都必须首先输入",(0,l.jsx)(e.code,{children:"pushy login"}),"进行登录。"]}),(0,l.jsxs)(e.p,{children:["至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅",(0,l.jsx)(e.a,{href:"integration",children:"代码集成"}),"。"]})]})]})}function t(s,e){throw Error("Expected "+(e?"component":"object")+" `"+s+"` to be defined: you likely forgot to import, pass, or provide it.")}e.default=(0,i.j)({MDXContent:function(){let s=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:e}=Object.assign({},(0,r.a)(),s.components);return e?(0,l.jsx)(e,{...s,children:(0,l.jsx)(c,{...s})}):c(s)},pageOpts:{filePath:"pages/docs/getting-started.mdx",route:"/docs/getting-started",frontMatter:{order:0,title:"安装配置",type:"快速入门"},title:"安装配置",headings:a},pageNextRoute:"/docs/getting-started"})}},function(s){s.O(0,[781,888,774,179],function(){return s(s.s=28675)}),_N_E=s.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/docs/getting-started-bb565f6f3bf98c17.js b/_next/static/chunks/pages/docs/getting-started-bb565f6f3bf98c17.js new file mode 100644 index 0000000..e13bd37 --- /dev/null +++ b/_next/static/chunks/pages/docs/getting-started-bb565f6f3bf98c17.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[153],{28675:function(s,e,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/docs/getting-started",function(){return n(48902)}])},48902:function(s,e,n){"use strict";n.r(e),n.d(e,{__toc:function(){return a}});var l=n(52676),i=n(31781),r=n(53743),o=n(58689);let a=[{depth:3,value:"安装",id:"安装"},{depth:3,value:"手动 link",id:"手动-link"},{depth:4,value:"iOS",id:"ios"},{depth:4,value:"Android",id:"android"},{depth:3,value:"配置 Bundle URL",id:"配置-bundle-url"},{depth:4,value:"iOS",id:"ios-1"},{depth:4,value:"Android",id:"android-1"},{depth:3,value:"禁用 android 的 crunch 优化",id:"禁用-android-的-crunch-优化"},{depth:3,value:"登录与创建应用",id:"登录与创建应用"}];function c(s){let e=Object.assign({p:"p",a:"a",code:"code",h3:"h3",pre:"pre",span:"span",h4:"h4",details:"details",summary:"summary",ol:"ol",li:"li",ul:"ul"},(0,r.a)(),s.components);return o.mQ||t("Tabs",!1),o.mQ.Tab||t("Tabs.Tab",!0),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsxs)(e.p,{children:["请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击",(0,l.jsx)(e.a,{href:"https://v9--pushy-site.netlify.app/",children:"这里"})]})}),"\n",(0,l.jsxs)(e.p,{children:["首先你应该有一个基于 React Native 开发的应用,我们把具有 package.json 的目录叫做你的",(0,l.jsx)(e.code,{children:"应用根目录"}),"。\n如果你还没有初始化应用,请参阅",(0,l.jsx)(e.a,{href:"https://reactnative.cn/docs/getting-started",children:"开始使用 React Native"}),"。"]}),"\n",(0,l.jsxs)(e.p,{children:["我们假设你已经拥有了开发 React Native 应用的一切环境,包括",(0,l.jsx)(e.code,{children:"Node.js"}),"、",(0,l.jsx)(e.code,{children:"Xcode"}),"、",(0,l.jsx)(e.code,{children:"Android SDK"}),"等等。"]}),"\n",(0,l.jsxs)(o.Rg,{children:[(0,l.jsx)(e.h3,{id:"安装",children:"安装"}),(0,l.jsx)(e.p,{children:"在你的项目根目录下运行以下命令:"}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"# 先全局安装命令行工具,每台电脑只用装一次"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"i"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"-g"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"react-native-update-cli"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"# 然后在项目目录中安装热更新模块"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"i"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"react-native-update"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"# 然后进入ios目录安装iOS模块"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"&&"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"pod"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"install"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "})]})}),(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsxs)(e.p,{children:["如果下载极慢或者显示网络失败,请设置使用淘宝镜像 ",(0,l.jsx)(e.code,{children:"npx nrm use taobao"})]})}),(0,l.jsx)(o.UW,{type:"warning",children:(0,l.jsxs)(e.p,{children:["注意请不要混用",(0,l.jsx)(e.code,{children:"npm/yarn/pnpm"}),"等包管理器及对应的",(0,l.jsx)(e.code,{children:"lock"}),"文件,团队成员请坚持使用同一包管理器,且仅保留统一格式的",(0,l.jsx)(e.code,{children:"lock"}),"文件"]})}),(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsx)(e.p,{children:"请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(使用 npx\nreact-native run-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。"})}),(0,l.jsx)(e.h3,{id:"手动-link",children:"手动 link"}),(0,l.jsx)(e.p,{children:"如果 RN 版本 >= 0.60 则不需要此手动 link 步骤。"}),(0,l.jsx)(o.UW,{type:"warning",children:(0,l.jsx)(e.p,{children:"注意:如果是混编 RN\n项目,或monorepo,或任何其他自定义的情况,由于自定义的配置可能不完整或不适应RN的目录结构,导致自动\nlink 的功能可能不能正常工作。此时即便 RN 版本 >= 0.60,你可能也需要执行手动\nlink 操作。"})}),(0,l.jsx)(e.h4,{id:"ios",children:"iOS"}),(0,l.jsxs)(e.details,{children:[(0,l.jsx)(e.summary,{children:"RN < 0.60且使用CocoaPods(推荐)"}),(0,l.jsxs)(e.ol,{children:["\n",(0,l.jsx)(e.li,{children:"在 ios/Podfile 中添加"}),"\n"]}),(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.code,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"pod 'react-native-update', path: '../node_modules/react-native-update'"})})})}),(0,l.jsxs)(e.ol,{start:"2",children:["\n",(0,l.jsxs)(e.li,{children:["在项目的 ios 目录下运行",(0,l.jsx)(e.code,{children:"pod install"})]}),"\n",(0,l.jsx)(e.li,{children:"重新编译"}),"\n"]})]}),(0,l.jsxs)(e.details,{children:[(0,l.jsx)(e.summary,{children:"RN < 0.60且不使用CocoaPods"}),(0,l.jsxs)(e.ol,{children:["\n",(0,l.jsxs)(e.li,{children:["在 XCode 中的 Project Navigator 里,右键点击",(0,l.jsx)(e.code,{children:"Libraries"})," ➜ ",(0,l.jsx)(e.code,{children:"Add Files to [你的工程名]"})]}),"\n",(0,l.jsxs)(e.li,{children:["进入",(0,l.jsx)(e.code,{children:"node_modules"})," ➜ ",(0,l.jsx)(e.code,{children:"react-native-update"})," ➜ ",(0,l.jsx)(e.code,{children:"ios 并选中"}),"RCTPushy.xcodeproj`"]}),"\n",(0,l.jsxs)(e.li,{children:["在 XCode 中的 project navigator 里,选中你的工程,在 ",(0,l.jsx)(e.code,{children:"Build Phases"})," ➜ ",(0,l.jsx)(e.code,{children:"Link Binary With Libraries"})," 中添加 ",(0,l.jsx)(e.code,{children:"libRCTPushy.a"}),"、",(0,l.jsx)(e.code,{children:"libz.tbd"}),"、",(0,l.jsx)(e.code,{children:"libbz2.1.0.tbd"})]}),"\n",(0,l.jsxs)(e.li,{children:["继续在",(0,l.jsx)(e.code,{children:"Build Settings"}),"里搜索",(0,l.jsx)(e.code,{children:"Header Search Path"}),",添加",(0,l.jsx)(e.code,{children:"$(SRCROOT)/../node_modules/react-native-update/ios"}),",勾选",(0,l.jsx)(e.code,{children:"recursive"}),"。"]}),"\n",(0,l.jsxs)(e.li,{children:["在",(0,l.jsx)(e.code,{children:"Build Phases"}),"添加一个",(0,l.jsx)(e.code,{children:"New Run Script Phase"}),"运行脚本,内容如下"]}),"\n"]}),(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"text","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"#!/bin/bash"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"set -x"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:'DEST="../node_modules/react-native-update/ios/"'})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:'date +%s > "$DEST/pushy_build_time.txt"'})})]})}),(0,l.jsxs)(e.ol,{start:"7",children:["\n",(0,l.jsxs)(e.li,{children:["尝试编译一下,顺利的话就会在",(0,l.jsx)(e.code,{children:"../node_modules/react-native-update/ios/"}),"文件夹下面生成一个",(0,l.jsx)(e.code,{children:"pushy_build_time.txt"}),"文件。然后在",(0,l.jsx)(e.code,{children:"Copy Bundle Resources"}),"里把生成的",(0,l.jsx)(e.code,{children:"pushy_build_time.txt"}),"文件添加进去。"]}),"\n"]})]}),(0,l.jsx)(e.h4,{id:"android",children:"Android"}),(0,l.jsxs)(e.details,{children:[(0,l.jsx)(e.summary,{children:"RN < 0.60 或其他不能自动 link 的情况"}),(0,l.jsxs)(e.ol,{children:["\n",(0,l.jsxs)(e.li,{children:["\n",(0,l.jsxs)(e.p,{children:["在",(0,l.jsx)(e.code,{children:"android/settings.gradle"}),"中添加如下代码:"]}),"\n",(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"text","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"include ':react-native-update'"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"project(':react-native-update').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-update/android')"})})]})}),"\n"]}),"\n",(0,l.jsxs)(e.li,{children:["\n",(0,l.jsxs)(e.p,{children:["在",(0,l.jsx)(e.code,{children:"android/app/build.gradle"}),"的 dependencies 部分增加如下代码:"]}),"\n",(0,l.jsx)(e.pre,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.code,{"data-language":"text","data-theme":"default",children:(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"implementation project(':react-native-update')"})})})}),"\n"]}),"\n",(0,l.jsxs)(e.li,{children:["\n",(0,l.jsxs)(e.p,{children:["打开",(0,l.jsx)(e.code,{children:"android/app/src/main/java/[...]/MainApplication.java"}),","]}),"\n"]}),"\n"]}),(0,l.jsxs)(e.ul,{children:["\n",(0,l.jsxs)(e.li,{children:["在文件开头增加 ",(0,l.jsx)(e.code,{children:"import cn.reactnative.modules.update.UpdatePackage;"})]}),"\n",(0,l.jsxs)(e.li,{children:["在",(0,l.jsx)(e.code,{children:"getPackages()"})," 方法中增加 ",(0,l.jsx)(e.code,{children:"new UpdatePackage()"}),"(注意上一行可能要增加一个逗号)"]}),"\n"]})]}),(0,l.jsx)(e.h3,{id:"配置-bundle-url",children:"配置 Bundle URL"}),(0,l.jsx)(e.p,{children:"注意此步骤无论任何版本,目前都需要手动配置。"}),(0,l.jsx)(e.h4,{id:"ios-1",children:"iOS"}),(0,l.jsx)(e.p,{children:"在你的 AppDelegate.mm 或 AppDelegate.m 文件(不同 RN 版本可能后缀名不同)中增加如下代码:"}),(0,l.jsx)(e.pre,{"data-language":"c","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"c","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ... 其它代码"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"AppDelegate.h"'})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"RCTPushy.h"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// <-- import头文件,注意要放到if条件外面"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 可能项目里有一些条件编译语句,例如有些版本RN自带的flipper"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// #if DEBUG"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:'// 注意**不要**在这里面引入"RCTPushy.h"'})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// #import "})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// #endif"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// rn 版本 >= 0.74 需要修改 bundleURL 方法"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"-"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" (NSURL "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")bundleURL"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#if"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"DEBUG"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新)"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"index"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"];"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#else"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" [RCTPushy bundleURL];"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <-- 把这里非DEBUG的情况替换为热更新bundle"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#endif"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// rn 版本 < 0.74 需要修改sourceURLForBridge方法"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"-"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" (NSURL "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")sourceURLForBridge:(RCTBridge "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")bridge"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#if"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"DEBUG"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新)"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"index"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"];"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#else"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" [RCTPushy bundleURL];"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <-- 把这里非DEBUG的情况替换为热更新bundle"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"#endif"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "})]})}),(0,l.jsx)(e.h4,{id:"android-1",children:"Android"}),(0,l.jsxs)(e.p,{children:["在 MainApplication 中增加如下代码(如果是混编原生的项目或其他原因没有使用 ReactApplication,请",(0,l.jsx)(e.a,{href:"api#updatecontextsetcustominstancemanagerreactinstancemanager-instancemanager",children:"使用此 api 集成"}),"):"]}),(0,l.jsxs)(o.mQ,{items:["Kotlin","Java"],children:[(0,l.jsx)(o.mQ.Tab,{children:(0,l.jsx)(e.pre,{"data-language":"kotlin","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"kotlin","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ... 其它代码"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓请注意不要少了这句import"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" cn.reactnative.modules.update.UpdateContext;"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"class"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" : "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Application"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"(), "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ReactApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"override"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"val"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" reactNativeHost: "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ReactNativeHost"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" object : "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"DefaultReactNativeHost"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"this"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:") {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"override"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"fun"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getJSBundleFile"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"(): "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"String"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"? {"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" UpdateContext."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getBundleUrl"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this@MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ...其他代码"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})})]})})}),(0,l.jsx)(o.mQ.Tab,{children:(0,l.jsx)(e.pre,{"data-language":"java","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"java","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ... 其它代码"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓请注意不要少了这句import"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"cn"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"reactnative"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"modules"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"update"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"UpdateContext"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:";"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"public"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"class"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"extends"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Application"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"implements"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ReactApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"private"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"final"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"ReactNativeHost"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" mReactNativeHost "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 老版本 RN 这里可能是 new ReactNativeHost(this)"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"new"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"DefaultReactNativeHost("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:")"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" @"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"Override"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"protected"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"String"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getJSBundleFile"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"() {"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"UpdateContext"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"getBundleUrl"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"MainApplication"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ↑↑↑"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// ...其他代码"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})})]})})})]}),(0,l.jsx)(o.UW,{type:"info",children:(0,l.jsx)(e.p,{children:"请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(npx react-native\nrun-ios 或 run-android 命令编译,或在 Xcode/Android Studio\n中重新编译)才能生效。"})}),(0,l.jsx)(e.h3,{id:"禁用-android-的-crunch-优化",children:"禁用 android 的 crunch 优化"}),(0,l.jsxs)(e.p,{children:["android 会在生成 apk 时自动对 png 图片进行压缩,此操作既耗时又影响增量补丁的生成。为了保证补丁能正常生成,您需要在",(0,l.jsx)(e.code,{children:"android/app/build.gradle"}),"中关闭此操作:"]}),(0,l.jsx)(e.pre,{"data-language":"gradle","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"gradle","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"android {"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" ..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" signingConfigs { ... }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" buildTypes {"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" release {"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" ..."})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" // 添加下面这行以禁用crunch"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" crunchPngs false"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"..."})})]})}),(0,l.jsx)(e.h3,{id:"登录与创建应用",children:"登录与创建应用"}),(0,l.jsxs)(e.p,{children:["首先请在 ",(0,l.jsx)(e.a,{href:"https://pushy-admin.reactnative.cn",children:"https://pushy-admin.reactnative.cn"})," 注册帐号,然后在你的项目根目录下运行以下命令:"]}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"login"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"email:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入你的注册邮"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"箱"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"password:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入你的密"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"码"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]})]})}),(0,l.jsxs)(e.p,{children:["这会在项目文件夹下创建一个",(0,l.jsx)(e.code,{children:".update"}),"文件,注意不要把这个文件上传到 Git 等 CVS 系统上。你可以在",(0,l.jsx)(e.code,{children:".gitignore"}),"末尾增加一行",(0,l.jsx)(e.code,{children:".update"}),"来忽略这个文件。"]}),(0,l.jsx)(e.p,{children:"登录之后可以创建应用。注意 iOS 平台和安卓平台需要分别创建:"}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"createApp"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"--platform"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"App"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"Name:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入应用名"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"字"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"createApp"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"--platform"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"android"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"App"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"Name:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入应用名"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"字"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]})]})}),(0,l.jsx)(o.UW,{type:"info",children:"两次输入的名字可以相同,这没有关系。"}),(0,l.jsx)(e.p,{children:"如果你已经在网页端或者其它地方创建过应用,也可以直接选择应用:"}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"pushy"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"selectApp"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"--platform"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"1"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:") 鱼多多("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"2"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:") 招财旺("}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:" "}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Total"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"2"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"ios"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"apps"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"Enter"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"appId:"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"输入应用前面的编"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"号"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]})]})}),(0,l.jsxs)(e.p,{children:["选择或者创建过应用后,你将可以在文件夹下看到",(0,l.jsx)(e.code,{children:"update.json"}),"文件,其内容类似如下形式:"]}),(0,l.jsx)(e.pre,{"data-language":"bash","data-theme":"default",children:(0,l.jsxs)(e.code,{"data-language":"bash","data-theme":"default",children:[(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"ios"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"{"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appId"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"1"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:","})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appKey"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"<一串随机字符串>"'})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" },"})}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"android"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:"{"})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appId"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"2"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string)"},children:","})]}),"\n",(0,l.jsxs)(e.span,{className:"line",children:[(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:'"appKey"'}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:":"}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,l.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"<一串随机字符串>"'})]}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:" }"})}),"\n",(0,l.jsx)(e.span,{className:"line",children:(0,l.jsx)(e.span,{style:{color:"var(--shiki-color-text)"},children:"}"})})]})}),(0,l.jsxs)(e.p,{children:["你可以安全的把",(0,l.jsx)(e.code,{children:"update.json"}),"上传到 Git 等 CVS 系统上,与你的团队共享这个文件,它不包含任何敏感信息。当然,他们在使用任何功能之前,都必须首先输入",(0,l.jsx)(e.code,{children:"pushy login"}),"进行登录。"]}),(0,l.jsxs)(e.p,{children:["至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅",(0,l.jsx)(e.a,{href:"integration",children:"代码集成"}),"。"]})]})]})}function t(s,e){throw Error("Expected "+(e?"component":"object")+" `"+s+"` to be defined: you likely forgot to import, pass, or provide it.")}e.default=(0,i.j)({MDXContent:function(){let s=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:e}=Object.assign({},(0,r.a)(),s.components);return e?(0,l.jsx)(e,{...s,children:(0,l.jsx)(c,{...s})}):c(s)},pageOpts:{filePath:"pages/docs/getting-started.mdx",route:"/docs/getting-started",frontMatter:{order:0,title:"安装配置",type:"快速入门"},title:"安装配置",headings:a},pageNextRoute:"/docs/getting-started"})}},function(s){s.O(0,[781,888,774,179],function(){return s(s.s=28675)}),_N_E=s.O()}]); \ No newline at end of file diff --git a/docs/api.html b/docs/api.html index f7a0970..83451e2 100644 --- a/docs/api.html +++ b/docs/api.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
文档
API参考

JavaScript 方法

+
文档
API参考

JavaScript 方法

new Pushy(options: PushyOptions)

创建 Pushy 热更新服务实例,其构造参数如下:

interface PushyOptions {
@@ -295,4 +295,4 @@ 

// ...各种setter,但注意不要调用setBundleAssetName .setJSBundleFile(UpdateContext.getBundleUrl(mContext, "assets://index.android.bundle")) .build(); -UpdateContext.setCustomInstanceManager(mReactInstanceManager);

\ No newline at end of file +UpdateContext.setCustomInstanceManager(mReactInstanceManager);
\ No newline at end of file diff --git a/docs/bestpractice.html b/docs/bestpractice.html index c6dcd3d..630cf7e 100644 --- a/docs/bestpractice.html +++ b/docs/bestpractice.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
文档
场景实践

优化原生和热更包体积

+
文档
场景实践

优化原生和热更包体积

iOS 原生包优化(ipa)

对于同一份 archive(其版本号、编译时间和内置 bundle 已固定,不会受导出方式所影响),可以用不同选项多次导出 ipa,选择其中最小的上传到 pushy 服务器作为热更基准包。

bitcode

@@ -106,4 +106,4 @@

// 否则还是走之前的询问流程 // Alert.alert('提示', '检查到新的版本....... }

-

又比如,可能某个版本包含一些重要的公告内容,所以还可以在上面插入一个公告字段等等。如何使用元信息,完全取决于您的想象力!

\ No newline at end of file +

又比如,可能某个版本包含一些重要的公告内容,所以还可以在上面插入一个公告字段等等。如何使用元信息,完全取决于您的想象力!

\ No newline at end of file diff --git a/docs/changelog.html b/docs/changelog.html index 1534226..c9b1057 100644 --- a/docs/changelog.html +++ b/docs/changelog.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
文档
更新日志

10.5.4(2024-04-23)

+
\ No newline at end of file +
\ No newline at end of file diff --git a/docs/cli.html b/docs/cli.html index 5755cc2..8aa1427 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
文档
命令行工具

安装

+
文档
命令行工具

安装

$ npm install -g react-native-update-cli

命令

pushy bundle

@@ -128,4 +128,4 @@

packageVersion: 要绑定的原生包版本名(四选一,需 cli 版本 1.7.2+)
  • minPackageVersion: 要绑定的最低原生包版本,大于等于此版本的将逐个绑定(四选一,需 cli 版本 1.27.0+)
  • maxPackageVersion: 要绑定的最高原生包版本,小于等于此版本的将逐个绑定(四选一,需 cli 版本 1.27.0+)
  • -

    \ No newline at end of file +
    \ No newline at end of file diff --git a/docs/faq.html b/docs/faq.html index aa64ac5..7070d93 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
    文档
    常见问题

    如果本页面没能回答您的疑问,您可以去issues 区 (opens in a new tab)或 QQ 群 729013783 提问,或给我们发邮件

    +
    文档
    常见问题

    如果本页面没能回答您的疑问,您可以去issues 区 (opens in a new tab)或 QQ 群 729013783 提问,或给我们发邮件

    业务问题

    热更新究竟能否上架?

    您可能听说过各种说法,但大量实践表明,热更新目前能够顺利上架 AppStore 和其他各种应用市场。唯一需要注意的是,在审核期间请不要发布热更新,不要让审核人员看到各种更新相关的提示和弹窗,即可顺利通过。

    @@ -116,4 +116,4 @@

    大客户VIP版在应用数量、原生包数量、热更包数量等方面配额与专业版相同。

    当前价格如下表:

    服务版本日均总热更查询次数价格(元/年)
    VIP1100 万至 1000 万次30000
    VIP21000 万至 5000 万次60000
    VIP35000 万至 1 亿次120000
    VIP41 亿次以上请联系客服
    -

    具体试用和对接请联系 QQ 客服 34731408

    \ No newline at end of file +

    具体试用和对接请联系 QQ 客服 34731408

    \ No newline at end of file diff --git a/docs/getting-started.html b/docs/getting-started.html index d42563f..53dbded 100644 --- a/docs/getting-started.html +++ b/docs/getting-started.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
    文档
    安装配置

    请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里 (opens in a new tab)

    +
    文档
    安装配置

    请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里 (opens in a new tab)

    首先你应该有一个基于 React Native 开发的应用,我们把具有 package.json 的目录叫做你的应用根目录。 如果你还没有初始化应用,请参阅开始使用 React Native (opens in a new tab)

    我们假设你已经拥有了开发 React Native 应用的一切环境,包括Node.jsXcodeAndroid SDK等等。

    @@ -73,7 +73,19 @@ // #endif -// 修改sourceURLForBridge方法 +// rn 版本 >= 0.74 需要修改 bundleURL 方法 +- (NSURL *)bundleURL +{ +#if DEBUG + // 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新) + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; +#else + return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle +#endif +} + + +// rn 版本 < 0.74 需要修改sourceURLForBridge方法 - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG @@ -135,4 +147,4 @@ "appId": 2, "appKey": "<一串随机字符串>" } -}

    你可以安全的把update.json上传到 Git 等 CVS 系统上,与你的团队共享这个文件,它不包含任何敏感信息。当然,他们在使用任何功能之前,都必须首先输入pushy login进行登录。

    至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅代码集成

    \ No newline at end of file +}

    你可以安全的把update.json上传到 Git 等 CVS 系统上,与你的团队共享这个文件,它不包含任何敏感信息。当然,他们在使用任何功能之前,都必须首先输入pushy login进行登录。

    至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅代码集成

    \ No newline at end of file diff --git a/docs/integration.html b/docs/integration.html index 67c113c..9521611 100644 --- a/docs/integration.html +++ b/docs/integration.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
    文档
    代码集成

    请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里 (opens in a new tab)

    +
    文档
    代码集成

    请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里 (opens in a new tab)

    安装配置完成后,确定应用编译顺利通过,下面我们来进行代码集成。

    获取 appKey

    检查更新时必须提供你的appKey,这个值保存在update.json中(使用pushy createApppushy selectApp命令后会自动生成),并且根据平台不同而不同。你可以用如下的代码获取appKey

    @@ -146,4 +146,4 @@

    }, });

    以上提及的所有 api 的说明文档可在这里查看。

    -

    现在,你的应用已经可以通过 pushy 服务检查版本并进行更新了。下一步,你可以开始尝试发布应用包和版本,请参阅发布热更新

    \ No newline at end of file +

    现在,你的应用已经可以通过 pushy 服务检查版本并进行更新了。下一步,你可以开始尝试发布应用包和版本,请参阅发布热更新

    \ No newline at end of file diff --git a/docs/publish.html b/docs/publish.html index 625fbe2..e3eea1e 100644 --- a/docs/publish.html +++ b/docs/publish.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
    文档
    发布流程

    现在你的应用已经具备了检测更新的功能,下面我们来尝试发布并更新它。流程可参考下图:

    +
    文档
    发布流程

    现在你的应用已经具备了检测更新的功能,下面我们来尝试发布并更新它。流程可参考下图:

    流程总结如下:

      @@ -81,4 +81,4 @@

      我们强烈建议您先发布一个测试包,再发布一个除了版本号以外均完全相同的正式包

      例如,假设我们有一个正式包,版本为1.6.0,那么可以修改版本号重新打包一个1001.6.0,以一个明显不太正常的版本号来标识它是一个测试版本,同时后几位相同,可以表明它和某个正式版本存在关联(内容/依赖一致)。

      在每次往发布包发起热更新之前,先对测试包1001.6.0进行更新操作,基本测试通过之后,再在网页后台上将热更包重新绑定到正式包1.6.0上。如果在测试包中发现了重大问题,你就可以先进行修复,更新测试确认通过后再部署到正式线上环境。这样,可以最大程度的避免发生线上事故。

      -

      万一确实发生线上事故需要回滚的话,首先利用版本控制系统回滚代码到正常的状态,然后重新生成热更包并推送即可。

    \ No newline at end of file +

    万一确实发生线上事故需要回滚的话,首先利用版本控制系统回滚代码到正常的状态,然后重新生成热更包并推送即可。

    \ No newline at end of file diff --git a/index.html b/index.html index 42fe4c8..fa51452 100644 --- a/index.html +++ b/index.html @@ -11,4 +11,4 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -

    他们选择了 Pushy

    网易游戏
    蓝月亮
    华中师范大学
    娇兰佳人
    平安不动产
    友车科技
    诺安基金
    锐捷网络
    航天信息
    天津公交
    Feature

    为什么选择 Pushy

    • img

      增量更新

      基于 bsdiff/hdiff 算法创建

      kb 级别超小更新包

    • img

      快捷发布

      命令行工具 & 网页双端管理

      支持CI部署

    • img

      极速下载

      基于阿里云高速CDN分发

      全国范围秒速更新

    • img

      稳定可靠

      自带崩溃回滚机制

      安全可靠

    • img

      灵活扩展

      开放定制元信息

      提供灵活自由的更新策略

    • img

      技术支持

      遇到技术问题?

      工作时间段内小时级别响应

      Icons made by Swifticons from www.flaticon.com

      Let’s Pushy

      # 安装
      $ npm i -g react-native-update-cli
      $ npm i react-native-update && cd ios && pod install

      # 上传原生基础包
      $ pushy uploadIpa yourApp.ipa
      $ pushy uploadApk yourApp.apk

      # 生成并上传热更包
      $ pushy bundle --platform android
      $ pushy bundle --platform ios
      查看文档
      \ No newline at end of file +

      他们选择了 Pushy

      网易游戏
      蓝月亮
      华中师范大学
      娇兰佳人
      平安不动产
      友车科技
      诺安基金
      锐捷网络
      航天信息
      天津公交
      Feature

      为什么选择 Pushy

      • img

        增量更新

        基于 bsdiff/hdiff 算法创建

        kb 级别超小更新包

      • img

        快捷发布

        命令行工具 & 网页双端管理

        支持CI部署

      • img

        极速下载

        基于阿里云高速CDN分发

        全国范围秒速更新

      • img

        稳定可靠

        自带崩溃回滚机制

        安全可靠

      • img

        灵活扩展

        开放定制元信息

        提供灵活自由的更新策略

      • img

        技术支持

        遇到技术问题?

        工作时间段内小时级别响应

        Icons made by Swifticons from www.flaticon.com

        Let’s Pushy

        # 安装
        $ npm i -g react-native-update-cli
        $ npm i react-native-update && cd ios && pod install

        # 上传原生基础包
        $ pushy uploadIpa yourApp.ipa
        $ pushy uploadApk yourApp.apk

        # 生成并上传热更包
        $ pushy bundle --platform android
        $ pushy bundle --platform ios
        查看文档
        \ No newline at end of file diff --git a/pricing.html b/pricing.html index b4a6580..44fa9d0 100644 --- a/pricing.html +++ b/pricing.html @@ -11,4 +11,4 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -

        新注册用户将自动获得7 天专业版免费试用评估。到期后转为免费版。如需按月购买,请联系QQ客服 34731408.

        免费版

        适用于小型应用,轻度更新需求

        0 / 年
        开始使用

        标准版

        适用于一般应用,中度更新需求

        800/ 年
        100/ 月
        • 可创建5个应用 ,每个限*:
        • 50个原生包,每个最大150M
        • 50个热更包,每个最大15M

        • 所有应用累加
          每天1万更新查询
        • 提供专人技术支持
        立即升级

        高级版

        适用于一般应用,中度更新需求

        2400/ 年
        300/ 月
        • 可创建10个应用,每个限*:
        • 60个原生包,每个最大500M
        • 60个热更包,每个最大50M

        • 所有应用累加
          每天10万更新查询
        • 提供专人技术支持
        立即升级

        专业版

        适用于商业应用,高速迭代需求

        7200/ 年
        900/ 月
        • 可创建50个应用,每个限*:
        • 100个原生包,每个最大2000M
        • 100个热更包,每个最大200M

        • 所有应用累加
          每天100万更新查询
        • 提供专人技术支持
        立即升级

        如您需要更高配额,我们也提供定制版本或是私有服务器部署,您可将具体需求发送至 hi@charmlot.com 我们将第一时间回复。

        *注:iOS 和 Android 版本记做不同的应用。
        原生包指完整的apk/ipa安装包。热更包指pushy bundle命令生成的ppk文件(不是用户实际下载的增量更新文件)。
        您可删除已不再使用的应用、原生包、热更包来有效利用配额。

        对于付费业务还有其他疑问?请参考常见问题

        \ No newline at end of file +

        新注册用户将自动获得7 天专业版免费试用评估。到期后转为免费版。如需按月购买,请联系QQ客服 34731408.

        免费版

        适用于小型应用,轻度更新需求

        0 / 年
        开始使用

        标准版

        适用于一般应用,中度更新需求

        800/ 年
        100/ 月
        • 可创建5个应用 ,每个限*:
        • 50个原生包,每个最大150M
        • 50个热更包,每个最大15M

        • 所有应用累加
          每天1万更新查询
        • 提供专人技术支持
        立即升级

        高级版

        适用于一般应用,中度更新需求

        2400/ 年
        300/ 月
        • 可创建10个应用,每个限*:
        • 60个原生包,每个最大500M
        • 60个热更包,每个最大50M

        • 所有应用累加
          每天10万更新查询
        • 提供专人技术支持
        立即升级

        专业版

        适用于商业应用,高速迭代需求

        7200/ 年
        900/ 月
        • 可创建50个应用,每个限*:
        • 100个原生包,每个最大2000M
        • 100个热更包,每个最大200M

        • 所有应用累加
          每天100万更新查询
        • 提供专人技术支持
        立即升级

        如您需要更高配额,我们也提供定制版本或是私有服务器部署,您可将具体需求发送至 hi@charmlot.com 我们将第一时间回复。

        *注:iOS 和 Android 版本记做不同的应用。
        原生包指完整的apk/ipa安装包。热更包指pushy bundle命令生成的ppk文件(不是用户实际下载的增量更新文件)。
        您可删除已不再使用的应用、原生包、热更包来有效利用配额。

        对于付费业务还有其他疑问?请参考常见问题

        \ No newline at end of file