ReactNative 与 Native 通信机制实践

ReactNative 2015年在FaceBook的React.js conf开源后,迅速的发展,迭代了许多版本,现在比较稳定了,前段时间首次在项目中首次使用了ReactNative 与Native 混编,在这里把自己遇到的问题以及解决方法跟大家分享一下,欢迎补充和指正。

由于Apple的强势,完全使用ReactNative 风险较高,比如AppleStore 审核时检测到bundle.js文件不让上架。出于稳健考虑,使用ReactNative 与Native混编。

总的来说RN混编包括几种方式:
1.总框架用RN,部分页面通过自定义原生组件,在ReactJs里调用。
2.总框架使用Native编写,部分灵活性较高的页面以及活动页面使用ReactNative。
3.还有在RN页面里嵌套Native页面以及在Native视图上添加RN子视图

我之前使用的Objective-C与Swift混编,个人偏向第二种方式。
具体的环境搭建,引入Native工程等在这里就不细说了。这里主要讲一下ReactNative与Native的通信

http://facebook.github.io/react-native/docs/getting-started.html

跨组件通信

属性 从Native传递属性到ReactNative
RCTRootView是一个UIView容器,承载着ReactNative页面,在其初始化方法里提供了一个接口用于传递参数。

    NSURL *jsCodeLocation;

  /**
   * Loading JavaScript code - uncomment the one you want.
   *
   * OPTION 1
   * Load from development server. Start the server from the repository root:
   *
   * $ npm start
   *
   * To run on device, change `localhost` to the IP address of your computer
   * (you can get this by typing `ifconfig` into the terminal and selecting the
   * `inet` value under `en0:`) and make sure your computer and iOS device are
   * on the same Wi-Fi network.
   */
  
    jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/connectUs.ios.bundle"];               

  
  /*
   *  属性传递参数
   */
   
    NSArray *imageList = @[@"http://foo.com/bar1.png",
                  @"http://foo.com/bar2.png"];

    NSDictionary *props = @{@"images" : imageList};
  
  
    RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                  moduleName:@"JCProject"
                                           initialProperties:props
                                               launchOptions:nil];

在JS文件中直接调用

renderImage: function(imgURI) {
return (
  <Image source={{uri: imgURI}} />
);
},
  render() {
    return (
      <View>
        {this.props.images.map(this.renderImage)}
      </View>
    );
}

RCTRootView 提供了一个读写权限的appProperties属性

NSArray *imageList = @[@"http://foo.com/bar3.png",
               @"http://foo.com/bar4.png"];
rootView.appProperties = @{@"images" : imageList};

可以随时更新属性,但更新必须在主线程中进行,读取可在任何线程中
属性的限制
跨语言属性的主要缺点是不支持回调方法,因而无法实现自下而上的数据绑定。设想你有一个小的RN视图,当一个JS动作触发时你想从原生的父视图中移除它。此时你会发现根本做不到,因为信息需要自下而上进行传递。

ReactNative 调用Native
对于ReactNative调Native 官方是这样说的

虽然我们有跨语言回调,但是这些回调函数并不总能满足需求。最主要的问题是它们并不是被设计来当作属性进行传递。这一机制的本意是允许我们从JS触发一个原生动作,然后用JS处理那个动作的处理结果。

//  遵循<RCTBridgeModule>代理,
@interface JCMoreViewController : JCBaseViewController<RCTBridgeModule>


//  在.m文件里实现 RCT_EXPORT_MODULE()方法,()中可以添加一个参数,指定在JS里访问这个模块的名字,如果不指定则默认使用类名
RCT_EXPORT_MODULE(jcModule);

// 声明通过通过 RCT_EXPORT_METHOD()宏来实现申明给Javascript导出的方法:
RCT_EXPORT_METHOD(setEvent:(NSString *)event location:(NSString *)location) {

 NSLog(@"Pretending to create an event %@ at %@", event, location);

}

在JS中你可以这用调用

  loadNative:function() {
    
     var JCMoreViewController = require('react-native').NativeModules.JCMoreViewController;
     
     JCMoreViewController.setEvent('ReactToNative', 'Success');
 }

此处有坑😄
通过这种方式,确实可以实现ReactJS调用Native方法,Log成功打印

RN调用Native成功

由于项目主要框架是以Objective-C与Swift原生写的,在ReactNative与Natvie 跳转中统一使用了Native的NavigationBar,由ViewController 作为视图容器,通过上面RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD()实现在ReactNative视图中,调用原生方法

RCT_EXPORT_METHOD(setEvent:(NSString *)event location:(NSString *)location)
{
    NSLog(@"Pretending to create an event %@ at %@", event, location);

    [self.navigationController popToRootViewControllerAnimated:YES];
}

通过Module写了一个跳转方法,然而并没有执行跳转,但是却打印了Log。

第一反应是不在主线程,在代码执跳转前面添加了

[[NSThread currentThread] setName:@"com.shen.stark"];
NSLog(@"mainThread =%d currentThread = %@",[NSThread isMainThread],[NSThread currentThread]);
当前线程
[self.navigationController performSelectorOnMainThread:@selector(popToRootViewControllerAnimated:) withObject:@YES waitUntilDone:YES];

在主线程里执行依然没有反应,

RCT_EXPORT_METHOD(setEvent:(NSString *)event location:(NSString *)location) {
NSLog(@"Pretending to create an event %@ at %@", event, location);
NSLog(@"mainThread =%d currentThread = %@",[NSThread isMainThread],[NSThread currentThread]);

dispatch_async(dispatch_get_main_queue(), ^{

NSLog(@"mainThread =%d currentThread = %@",[NSThread isMainThread],[NSThread currentThread]);
//isMainThread = 1    在主线程

[self.navigationController popToRootViewControllerAnimated:YES];

NSLog(@"ping");    //正常打印
});
}
线程信息

主线程中跳转后面的Log都打印了,排除了线程问题,有可能是视图层次问题?获取不到self.navigationController

对象属性为null

果然,打印了self.navigationControllerself.title以及其他已赋值的属性,都是空。

ViewDidLoad中打印的对象的内存地址

在上一级的JCMoreViewController的Native页面的ViewDidLoad里打印了self对象,发现和ReactNative调用时的对象内存地址不一致,由此可见RCT_EXPORT_MODULE暴露modluejs之后,会重新创建这个对象,导致其属性都是nil;

目前找的的解决方案是利用单例创建对象保证创建对象 的唯一性,个人比较喜欢用程序自带的AppDelegate这个系统单例,因为视图的结构是 4个 NavigationController放在 TabBarController上,这四个NavigationController可以只创建一次,在AppDelegate里初始化,然后添加到TabBarController上。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,448评论 25 707
  • ​ React Native(以下简称RN)的目标是用基于react的JavaScript写代码,在iOS/A...
    Iceguest阅读 3,540评论 0 10
  • 现在工作生活的忙碌,让我们很少有时间能停下来审视自己的内心。忙碌的工作为了获得肯定、得到晋升;辛勤的生活为了收获友...
    有实阅读 366评论 2 1
  • 勿以西南远, 忘忧当树萱。 初晴带晓凉, 心贞徒见赏。 方以称文明, 得其刚且劲。 始见春江阔, 终异再经过。
    快乐享瘦阅读 299评论 0 1
  • 一个人只有把自己融入集体中,才能最大程度地实现个人的价值,绽放出完美绚丽的人生。认识自己的不足,善于看到别人--尤...
    积分制管理阅读 293评论 0 1