zoukankan      html  css  js  c++  java
  • 关于RN的热更新

    写点关于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

  • 相关阅读:
    跨域资源共享 CORS 详解以及IIS中的配置方法
    c#创建文件夹时无法访问路径,路径拒绝访问
    C#接口在派生类和外部类中的调用
    .NET/C#识别用户访问设备
    另一个SqlParameterCollection中已包含SqlParameter
    Java中throw和throws的区别
    MySQL与Oracle的语法区别详细对比
    hibernte中用criteria实现not in功能的方法
    使用Hibernate SQLQuery执行原生SQL
    hibernate查询数据表char类型字段只返回一个字符
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12258852.html
Copyright © 2011-2022 走看看