写点关于RN的热更新和RN版本升级后的强制更新。以及优化白屏问题

在APPDelegate中加载RN,一般的加载方式是:
RCTRootView *rootView= [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"authen_native" initialProperties:nil launchOptions:nil];

1
2
3
4
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions;

但在调试中发现两个现象:
1.重复进入react-native页面、退出react-native页面的操作,RCTBridge对象会被重复创建、销毁。有时候RCTBridge对象未能及时创建还会crash
2.在原生页面和react-native页面相互跳转是RCTBridge也会被重复创建,造成很大的内存开销

阅读RCTRootView.h发现一些细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 * - Convenience initializer -
* A bridge will be created internally.
* This initializer is intended to be used when the app has a single RCTRootView,
* otherwise create an `RCTBridge` and pass it in via `initWithBridge:moduleName:`
* to all the instances.
*/
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions; * - Designated initializer -
*/
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;

initWithBundleURL与initWithBridge的区别

对于项目中只有一个RCTRootView的时候建议initWithBundleURL的方法,这个方法内部创建了一个RCTBridge.

而有多个RCTRootView的情况,建议initWithBridge的方法.开发者直接创建RCTBridge,多个RCTRootView可共用一个RCTBridge。

项目中使用多个RCTRootView,推荐使用以下方法initWithBridge初始化:

1
2
3
4
5
6
 _bridge = [[RCTBridge alloc] initWithBundleURL:[SDRrectFileOption SetFileWithOption:self.luanchOption]
moduleProvider:nil
launchOptions:self.luanchOption];
RCTRootView = [[RCTRootView alloc] initWithBridge:_bridge
moduleName:@"authen_native"
initialProperties:nil];

在SDRrectFileOption中返回的是jsbundle的地址。在这个文件中可以使用NSFileManager来把jsbundle缓存到本地。但是如果是新版本的RN比如0.57要替换老版本的比如0.54的APP覆盖更新的话,记得要对比版本号,然后把缓存里面的jsbundle清除掉再返回新的jsbundle地址。不然会导致crash。

RN的热更新

在APPdelegate的didFinishLaunchingWithOptions方法中来判断是否需要Update。在Update方法中如果需要强制更新的话就就把RCTBridge调用reload方法进行热更新—和初始化使用的是同一个bridge。

1
2
3
4
5
6
7
8
- (void)checkUpdate {
patchClass *patch = [patchClass sharedInstance];
[patch checkUpdate];
patch.IS_COERCIVE = ^(NSURL *newPath) {
//是强制更新的话,就把RCTBridge调用reload方法进行热更新
[_bridge reload];
};
}

在patchClass中使用的是单例,在这个里面通过接口判断是否需要热更还是强制更新,是只更新jsbundle还是整包更新,下载文件,把下载的压缩文件解压缩,如果缓存里面有文件先删除旧的jsbundle再保存,

大专栏  关于RN的热更新ink" title="解决白屏问题">解决白屏问题

使用单例初始化一个bridge对象解决上述问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//.h
@interface : RCTBridge + (BridgeManager*)shareInstance;
@end @interface BridgeHandle : NSObject<RCTBridgeDelegate> @end //.m
implementation MallBridgeHandle - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
return [[NSBundle mainBundle] URLForResource:@"index.ios" withExtension:@"jsbundle"];
}
@end @implementation
static BridgeManager * manager = nil;
static dispatch_once_t onceToken;
+ (BridgeManager*)shareInstance
{
dispatch_once(&onceToken,^{
manager = [BridgeManager alloc] initWithDelegate:[[BridgeHandle alloc] init] launchOptions:nil];
});
return manager ;
}
@end

单例在程序启动时初始化。
测试验证可以发现:内存得到优化,白屏问题得到解决。

桥接原生模块

首先我们需要创建一个类,然后导入头文件 #import <RCTBridgeModule.h> ,这个类需要实现 RCTBridgeModule 协议。

1
2
3
4
5
6
#import <Foundation/Foundation.h>
#import <RCTBridgeModule.h> @interface RNTestManager : NSObject <RCTBridgeModule> @end

模块名字

在类的实现部分,需要包含 RCT_EXPORT_MODULE() 宏,这个宏也可以添加一个参数用来指定在 JS 中访问这个模块的名字。如果你不指定,默认就会使用这个 OC 类的名字。

导出方法

RCT_EXPORT_METHOD(),导出到 JS 的方法名是 OC 的方法名的第一个部分,桥接到 JS 的方法返回值类型必须是 void。RN 的桥接操作是异步的,所以如果要返回结果给 JS,你必须通过回调或者触发事件来进行。传入的参数类型有以下几种:

string (NSString)
number (NSInteger, float, double, CGFloat, NSNumber)
boolean (BOOL, NSNumber)
array (NSArray) 包含本列表中任意类型
object (NSDictionary) 包含string类型的键和本列表中任意类型的值
function (RCTResponseSenderBlock)

回调函数
RCT_EXPORT_METHOD(RNInvokeOCPromise:(NSDictionary *)dictionary resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject){
}
桥接原生方法的最后两个参数是RCTPromiseResolveBlock 和RCTPromiseRejectBlock的话,则对应的JS方法就会返回一个Promise对象。

设置原生模块执行操作的线程
如果你在原生模块中需要更改 UI 或者必须在主线程的话,可以实现

- (dispatch_queue_t)methodQueue 方法

- (dispatch_queue_t)methodQueue {
return dispatch_get_main_queue();
}

RN使用原生的View

import入UIView+React.h文件,,原生视图都需要被一个RCTViewManager的子类来创建和管理。这些管理器在功能上有些类似“视图控制器”,但它们本质上都是单例 —— React Native只会为每个管理器创建一个实例。步骤:

  1. 创建一个子类
  2. 添加 RCT_EXPORT_MODULE()标记宏
  3. 实现 -(UIView *)view 方法

创建一个子类

该组件有回调需要处理,这里即必须用到 RCTDirectEventBlock 或者 RCTBubblingEventBlock,而且命名的时候要特别注意,需要已 on 开头,熟悉 JS 的朋友应该会反应过来,这很像 JS 的事件命名规范。

@property (nonatomic, copy) RCTBubblingEventBlock onValueChange;
@property (nonatomic, copy) RCTBubblingEventBlock onSlidingComplete;

在自定义ViewManager中

  1. 初始化子View
  2. 添加 RCT_EXPORT_MODULE()标记宏
    添加TYRCBarChartViewManager 来管理TYRCBarChartView。这个TYRCBarChartViewManager : 继承自RCTViewManager。 RCTViewManager 实现 RCTBridgeModule 协议。
  3. 自定义属性RCT_CUSTOM_VIEW_PROPERTY
  4. 自定义方法RCT_EXPORT_METHOD(refresh){
    [_barView refreshData];
    }
    RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass),完整的属性定义为

给JS发送事件使用 eventDispatcher

[self.rootView.bridge.eventDispatcher sendAppEventWithName:@”deviceLocalStateChange”
body:@{@”state”:state}];

参考资料

ios2.1大礼包被拒经验分享https://zhuanlan.zhihu.com/p/54042709

关于RN的热更新的更多相关文章

  1. ReactNative学习笔记(四)热更新和增量更新

    概括 关于RN的热更新,网上有很多现成方案,但是一般都依赖第三方服务,我所希望的是能够自己管控所有一切,所以只能自己折腾. 热更新的思路 热更新一般都是更新JS和图片,也就是在不重新安装apk的情况下 ...

  2. RN学习1——前奏,app插件化和热更新的探索

    react_native_banner-min.png React Native(以下简称RN)有大量前端开发者的追捧.前端开发是一个活跃的社区,一直尝试着一统前后端,做一个全栈开发,RN就是他们在客 ...

  3. iOS热更新技术被苹果官方警告?涉及到RN、Weex、JSPatch!!!

    今天一早,不少iOS开发群都炸窝了,原因是部分iOS开发者收到了苹果的警告邮件: 有开发者质疑可能是项目中使用了JSPatch.weex以及ReactNative等热更新技术.对于修复bug提交审核的 ...

  4. RN热更新

    说白了集成RN业务,就是集成RN离线包,解析并渲染.所以,RN热更新的根本原理就是更换js bundle文件和资源文件,并重新加载,新的内容就完美的展示出来了. 目前市场上出现的3种热更新模式如下:仅 ...

  5. iOS热更新技术被苹果官方警告?涉及到RN、Weex、JSPatch

    本文为转载文章 故事背景: 这两天,不少iOS开发群都炸窝了,原因是部分iOS开发者收到了苹果的警告邮件: 有开发者质疑可能是项目中使用了JSPatch.weex以及ReactNative等热更新技术 ...

  6. ReactNative 告别CodePush,自建热更新版本升级环境

    微软的CodePush热更新非常难用大家都知道,速度跟被墙了没什么区别. 另外一方面,我们不希望把代码放到别人的服务器.自己写接口更新总归感觉安全一点. so,就来自己搞个React-Native A ...

  7. React Native热更新(iOS)-Pushy

    React Native的出现,使的开发iOS代码出现了更便捷的方式.由于RN是使用脚本语言编写的,实现了"解释执行"的方式,而这种执行方式的修改只需替换脚步即可,不需要重新发布程 ...

  8. 移动端热更新方案(iOS+Android)

    PPT资源包含iOS+Android 各种方案分析:https://github.com/qiyer/Share/blob/master/%E7%83%AD%E6%9B%B4%E6%96%B0%E5% ...

  9. CodePush热更新组件详细接入教程

    CodePush热更新组件详细接入教程 什么是CodePush CodePush是一个微软开发的云服务器.通过它,开发者可以直接在用户的设备上部署手机应用更新.CodePush相当于一个中心仓库,开发 ...

随机推荐

  1. CodeForces 990B Micro-World(思维、STL)

    http://codeforces.com/problemset/problem/990/B 题意: 有n个细菌,每个细菌的尺寸为ai,现在有以常数k,如果细菌i的尺寸ai大于细菌j的尺寸aj,并且a ...

  2. Filter过滤器的应用

    Filter过滤器作用:在每次请求服务资源时做过滤处理. 原理:Filter接口中有一个doFilter方法,当开发人员编写好Filter类实现doFilter方法,并配置对哪个web资源进行拦截后, ...

  3. pandas(一)

    pandas.io 1.概述,主要从txt,json,pkl,csv,excel中读取数据,读取的数据最终转化为pandas.core.frame.DataFrame类型的df 先来看总的api fr ...

  4. 903C. Boxes Packing#俄罗斯套娃问题(map使用)

    题目出处:http://codeforces.com/problemset/problem/903/C 题目大意:求这组数据中数据出现的最大重复次数 #include<iostream> ...

  5. 十六、linux系统网络基础

    1.网络是由IP构成的:network + host,以至于我们使用网络向外发信息,不会发错. 2.子网掩码这里要知道两点: 1)子网掩码不可能出现交叉部分,换句话说不会出现01010101的交叉现象 ...

  6. Sam format

    reference:https://davetang.org/wiki/tiki-index.php?page=SAM @SQ SN:contig1 LN:9401 (序列ID及长度) 参考序列名,这 ...

  7. Apsara Clouder云计算专项技能认证:网站建设-部署与发布

    一.课程学习介绍和学习目标 1.学习内容 掌握如何将一个本地已经设计好的静态网站发布到Internet公共互联网,通过自己的域名让全世界的网民访问到,如何完成工信部的ICP备案,实现监管合规. 2.学 ...

  8. Java 线程池(二)

    简介 在上篇 Java 线程池(一) 我们介绍了线程池中一些的重要参数和具体含义,这篇我们看一看在 Java 中是如何去实现线程池的,要想用好线程池,只知其然是远远不够的,我们需要深入实现源码去了解线 ...

  9. 前端-css-长期维护

    ###############    CSS简介    ################ # CSS # HTML是骨架 # CSS是样式 # JS是动作 # css和html是分成两个文件编写的,这 ...

  10. python模块之shelve,xml,hashlib,configpaser

    shelve shelve模块也是一种可以将数据序列化的模块 使用方法 1. open 2. 读写 3. close 特点:使用方法比较简单 提供一个文件名字就可以开始读写,读写的方法和字典一致;跨平 ...