Open rccoder opened 7 years ago
热更新彻底被 Apple 禁了额,-。-
@JimmyLv 应该属于媒体想搞大新闻吧,讲道理是没有禁止的吧
https://www.zhihu.com/question/60767945
感觉像 ReactNative 这种实际上 Bundle 就是一个描述文件,这种禁止的话 H5 离线包的是不是也要禁止了 😂 感觉不大可能
sync 热更新下载成功后 重启app 报错。有遇到过这种情况吗?错误信息: 07-19 14:53:45.960 19585-19760/? E/AndroidRuntime: FATAL EXCEPTION: mqt_js Process: com.userviceapp, PID: 19585 java.lang.RuntimeException: Error calling AppRegistry.runApplication at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:29) at android.os.Looper.loop(Looper.java:135) at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:192) at java.lang.Thread.run(Thread.java:818) Caused by: com.facebook.jni.CppException: Could not get BatchedBridge, make sure your bundle is packaged correctly at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)? at android.os.Handler.handleCallback(Handler.java:739)? at android.os.Handler.dispatchMessage(Handler.java:95)? at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:29)? at android.os.Looper.loop(Looper.java:135)? at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:192)? at java.lang.Thread.run(Thread.java:818)?
一、背景
我的毕业设计是用 React Native 写一款校园 APP,服务端采用 egg + MongoDB。
选用 React Native 一来是想借助他更加的学习巩固 React、Redux 生态系统;二来是做成 APP 而不是网站会在老师面前显得不是那么的 Low,同时借助双平台为忽悠填一份色彩;三来是 React Native 确实在性能上是优于 H5,不需要 XX 内核(如 UC、QQ等)来抹平杂乱机型的性能与兼容问题,同时还能和 H5 一样保持热更新。
如果感兴趣的话,代码在这里:
二、热更新原理
2.1、引言
React Native 的原理大致是上层写 React 式的代码,然后利用相关的 loader 打包成 bundle 和相关的静态文件,然后利用 Android、iOS 里的 SDK 解析 bundle,然后以 Native 的方式执行。
在大型 APP 中,针对 H5 中的静态文件会设置离线包,以达到秒出加速的目的,当然离线包的增大会导致 APP 体积的增大。针对一些场景(比如营销页面等),并不希望离线加载,这样的场景就不使用离线包进行加载。
离线包也需要进行更新,这里就需要一套比较完善的更新机制来保证(大公司自己造,小公司找有没有开源的)。
2.2、正文
热更新指的就是离线包(React Native 中的 bundle)更新的这个机制。我们可以在 APP 运行的过程中 “偷偷” 下载 bundle,然后在下次 APP 开启的时候(或者某种自定义的时机,比如:弹窗提示、直接重启等)使用新的 bundle。
三、CodePush 介绍
如何去维护这样一个比较完善的更新机制呢?Microsoft 给出了一个很好的答案 —— CodePush。幸运地,他还没被“墙“,我们可以直接的使用他的服务。
CodePush 集成了微软的一个云服务器,他相当于是一个中心发布器,APP 可以询问他是否有新的 bundle 更新,然后进行下载等操作。
CodePush 官方提供了 React Native 的集成方案,可以比较容易的集成到原有代码中(当然,也存在一些坑或者其他的地方,这也是本来出现的目的之一)。
四、实施流程
1、CodePush 服务配置
CodePush 的官网在 这。
1.1、安装 CodePush CLI
1.2、注册 CodePush
会自动打开浏览器弹出注册界面,注册完成之后会得到一个 token,复制后填写在 Terminal 里面即可。
1.3、注册应用
每个 APP 都会得到
Production
与Production
状态的两个 Key:2、SDK 集成
SDK 的集成包含三部分,分别是 JS 源码、iOS 客户端、Android 客户端。
SDK 直接用 CodePush 官方发布的 [react-native-code-push]() 即可。
值得注意是,react-native-code-push 最近发布的 2.0-beta 版本,而改版本只支持 React Native 0.43 及其以上的版本中。如果是 0.43 以下版本的需要使用 1.17.x(最新 1.17.4-beta)。
我这里使用的 RN 版本是 0.40,所以安装 1.17.4-beta 版本的 react-native-code-push。
在进行下面的三个之前,我们先用 React Native 使用的 rnpm(React Native Package Manage) link 以下这个包 (自动改变 ios 和 android 目录下的一些文件)。
期间会提示你输入 iOS 和 Android 端的 key,这里输入你用 CodePush 注册 APP 时产生的 key,这里我们先可以都输入每个 Staging 的 key。
如果不幸你忘记了之前产生的 key,可以输入以下的命令查看:
2.1、React Native 源码集成
在 React Native 源码中,我们需要引入
react-native-code-push
,然后用 CodePush 包裹一下最外层的组件。没有 CodePush 之前我们的代码这样写:
现在我们只需要改动一下
src/index.js
,用 CodePush 包裹以下这个组件即可:这里
checkFrequency
定义了何时进行 bundle 更新,这里ON_APP_RESUME
是指在 APP 重新打开的时候进行更新替换。更加详细的情况可以参加 react-native-code-push 的 example 进行编写。目前这样仅仅是够用。2.2、 iOS SDK 集成与打包
2.2.1、rnpm 自动配置
在上面使用 rnpm link 之后,其实大部分的问题都已经基本解决了。
这里你需要打开 git diff 看一下 rnpm link 有没有做一些 ”坏事“ 可能会对你的代码造成影响。
这里我们发现他改变了下面几个文件:
重点看一下是在可辨识的范围内修改了
Info.plist
,在里面加入了 key 方便调用。除此之外还重点修改了一下
AppDelegate.m
:这是指在 debug 包中继续之前的老套路远程加载 bundle,在 release 模式中托管给 CodePush 管理 bundle 的加载。
对,这和我们之前预想的完全一样。
接着,用 Xcode 打开 iOS 工程,检查一下版本号是不是三位,如果不是的话,修改为三位(比如:1.0.0):
2.2.2、忽略 Test
这部分你可以先跳过,直接看 build 部分,如果有问题再回开看。
我在 build 的时候发生了错误,log 如下:
这看起来时测试出了问题,忽略掉他即可。
commmand + shift + ,
,在弹出的框里面选择 build,然后取消掉 Test 部分的勾。然后继续 build。
2.2.3、打build 包
在之前的
AppDelegate.m
中,我们代码的意图是在debug
模式下通过远程加载 bundle,在release
模式下用 CodePush 来处理。这样保证了我们在开发的时候不受 CodePush 的影响,所以要测试 CodePush,就打在release
包。command + shift + ,
,在 run 里面Build Configuration
里面选择 release。然后
command + R
或者点击按钮进行打包。理论上你会注意到打了一个不需要加载远程 bundle 的包。
2.3、Android SDK 集成与打包
2.2.1、rnpm 自动配置
和 iOS 端一样,利用 rnpm link 就能解决大部分的问题。rnpm link 之后,我们 git diff 一下看看有哪些变动:
核心是修改了上面的几个文件:
自动在
setting.gradle
里面加入了 CodePush:自动在
build.gradle
加入了 CodePush 的编译依赖:自动在
string.xml
里面加入了 key,方便 java 代码中的调用。自动在
MainActivity.java
里面引入的 CodePush。自动在
MainApplication.java
中重写的getJSBundleFile
函数。到这里,进行 run 的时候会提示找不到 bundle,出现下面的错误:
模拟器也是红红的提示找不到 bundle。
这和我们猜测的一直,整个 rnpm link 之后的代码中没有找到如何使用那个 key。
参考官方 example 中的代码,最终找到要在
MainApplication.java
中使用那个 Key:那个 id 是
string.xml
里面的 id。这样重新进行 run,可能会发现又提示:
模拟是也是大大的红色错误提示:
参考 React Native 的 issues#9336 和 stackoverflow。
不要使用 Android Studio 启动,使用:
很好,能跑起来了!
2.3.2、打 build 包
现在已经打了 debug 的包,那如何打一个 release 包来测试我们的 CodePush 呢?
2.3.2.1、Android Studio 生成证书 jks 文件
首先我们需要一个签名,关于这个签名,我们在开发的时候实际上是使用了 Android Studio 自带的一个 debug 签名,在打 release 包的时候,我们就需要自己签名了:
如下图,在 build 里面选择 Generator Singed Apk:
然后点击
Create New...
:然后填写即将生成的 patch 路径,密码,Alias的名字,密码,还有一些公司个人相关的东西:
OK,看看你填写的证书路径里是否有一个 xx.jks 的证书了。通过这个证书,我们就可以得到 App 的 Sha1 等用于一些第三方 SDK,更多细节可以参考 获取Android SHA1 、生成jks密钥、签名Apk
2.3.2.2、Android Studio 自动打包
理论上在刚才的基础下点击下一步,选择
Release
包就能得到 Release 包了,但用:安装之后,打开会直接 crash(需要先卸载之前的 APP),解压 APK,发现 asset 里面没有和 CodePush 有关的任何东西,猜测可能是这里引起 Crash。
所以这种打包方式不能用。
部分解释参见: http://www.jianshu.com/p/1cff76e20ede。
2.3.2.3、手动配置 Gradle 进行自动打包
找到之前生成的证书,复制到 app 目录下,比如我的证书叫
WeHIT.jks
。接着修改
android/gradle.properties
,加入证书的相关信息,方便其他文件调用:然后在
app/build.gradle
里配置打包签名:这里会用到之前保存在
android/gradle.properties
里的证书信息。然后,切换到 andriod 目录下执行:
在 app/build/outputs/apk 里面会得到 release 包:
2.3.2.4、额外小记
在运行模拟器的时候,提示:
发现是 Docker 这种虚拟机在跑,关掉即可正常打开模拟器。
3、CodePush 推送新 bundle
改完代码后,我们需要更新 bundle,这样就需要我们打包然后把 bundle 推送到 CodePush 服务器上。
3.1、打包推送一步流
在项目根目录执行:
Android 推包
iOS 推包
看到 log:
表示已经成功。
3.2、打包推送两步流
上面的一步流是把 打包和推送 结合到了一起,从 log 中也能简单看到先是执行打包,然后在 Push 的。
在 APP 的测试包中,我们可能希望能保留开发 Log 等,或者说我们想比较好的自定义 history 等。这样就需要我们手动的两次进行打包和推送了。
3.2.1、打包
首先我们创建一个 bundles 文件夹来存储打包后的 bundles
然后进行打包
这样就在 bundles 文件夹下面生成 bundle
3.2.2、推送
bundle 已经打好,现在需要推送到 CodePush 平台上。
3.3、暗中观察
如果不想看这么多的版本,可以用:
同时,你会发现装了 release 包的客户端会自动更新。
四、后记
如果之前没有客户端的开发经验,配置 CodePush SDK 还是有点复杂,如果你在配置过程中遇到了什么问题,或者发现文章中有错误,欢迎指出。
五、参考文章