React Native和Native间的通信

本系列文章作为学习RN期间的总结

刚开始接触RN,有几点大家都想弄清楚:

  • 如何从Native向RN传递参数,这里也分为几小点

    1. 在初始化RN的时候如何从Native传递初始化的参数
    2. 在RN视图已经渲染完成之后,如果从Native那参数
  • 如何从RN向Native传递参数:

    1. 如何向Native发送消息(能否使用闭包)
    2. 如何让RN接受naitve的异步回调

Native -> RN

Properties

properties是RN和Native间通信最简单的方式。我们只需要在初始化RCTRootView的时候,将需要的数据通过方法入参的形式传入,它是一个字典类型。

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:{{< hl-text red >}}(NSDictionary *)initialProperties{{< /hl-text >}}
                    launchOptions:(NSDictionary *)launchOptions

在RN中我们用来承接initialProperties参数的就是每个js类的props。如果我们需要在Native中修改props属性,就要对RCTRootViewappProperties进行重新赋值,然后RN会对视图进行重新渲染,当且仅当两次赋值不一样的时候才会重新渲染。

rootView.appProperties = @{@"content" : imageList};

我们也可以使用

- (instancetype)initWithBridge:(RCTBridge *)bridge
                    moduleName:(NSString *)moduleName
             initialProperties:(NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;

在初始化bridge时以代理的方式将bundleURL传入:

- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
                   launchOptions:(NSDictionary *)launchOptions;


 @protocol RCTBridgeDelegate <NSObject>

/**
 * The location of the JavaScript source file. When running from the packager
 * this should be an absolute URL, e.g. `http://localhost:8081/index.ios.bundle`.
 * When running from a locally bundled JS file, this should be a `file://` url
 * pointing to a path inside the app resources, e.g. `file://.../main.jsbundle`.
 */
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge;

@optional 

需要注意的是,赋值操作必需要放在Native的主线程。而且js中的componentWillReceivePropscomponentWillUpdateProps并不会因为重新渲染而被再次调用,你只能在componentWillMount方法中访问新的props

RCTEventEmitter

我们可以使用RCTEventEmitter类来让js监听native的事件,facebook的文档中是这样描述RCTEventEmitter的:

/**
 * RCTEventEmitter is an abstract base class to be used for modules that emit
 * events to be observed by JS.
 */

它是一个模块的抽象基类,用来发送被JS监听的事件。我们通过

- (void)sendEventWithName:(NSString *)name body:(id)body

方法来完成事件的发送。入参是事件名称和需要携带的参数(Dictionary)。
每一个RCTRootView都有一个RCTBridge属性,它是RN中非常重要的一个类,负责Native和JS之间的通信。通过RCTBridge的:

/**
 * Retrieve a bridge module instance by name or class. Note that modules are
 * lazily instantiated, so calling these methods for the first time with a given
 * module name/class may cause the class to be sychronously instantiated,
 * potentially blocking both the calling thread and main thread for a short time.
 */
- (id)moduleForName:(NSString *)moduleName;

方法,我们可以拿到每个RCTRootView对应的RCTEventEmitter实例(它是通过lazy load的方式初始化的),因为这里的moduleName参数可以是类名也可以是自定义的模块名称,所以在项目中如果出现多个实例,需要在初始化通过view的reactTag属性来区分。该属性声明在分类UIView+React.h中。

RCT_EXPORT_MODULE();

+ (EventEmitterManger *)mangerWithRootView:(RCTRootView *)rootView {
    return [rootView.bridge moduleForName:NSStringFromClass(self)];
}

这样我们就拿到目标RootView对应的RCTEventEmitter对象。接下来我们需要为事件定义一个identifier,重写

/**
 * Override this method to return an array of supported event names. Attempting
 * to observe or send an event that isn't included in this list will result in
 * an error.
 */
- (NSArray<NSString *> *)supportedEvents;

方法,把你方法的identifer作为数组的一个元素返回。这样就可以使用上面所说的方法进行消息的发送了。
到这里Native端的代码已经完成了,下面看在js中如何设置。
我们需要在对应的js中引入一个NativeModulesNativeEventEmitter模块,然后获取监听模块的manger

var raToRnManger = NativeModules.EventEmitterManger

在js对象的声明周期方法componentWillMount中设置监听,接下来就等native发送消息了。

componentWillMount(){
         var raToRnMangerEmitter = new NativeEventEmitter(raToRnManger)
          const subscription = raToRnMangerEmitter.addListener("EventReminder",
              (reminder) => {
                  console.log("test")
                    this.setState({
                        name:"B"
                    })
              }
          );
    }

RN -> Native

RCTBridgeModule

在Native端定义RN模块的时候,让其遵循<RCTBridgeModule>协议,在实现文件中暴露出你想要从RN向Native发送的消息

RCT_EXPORT_METHOD(exampleMethod:(NSString *)name)
{
    NSLog(@"%@",name);
}

如果你想要该方法参数支持闭包,可以使用RN内置的闭包类型RCTResponseSenderBlock

RCT_EXPORT_METHOD(exampleCallbackMethod:(RCTResponseSenderBlock)callback)
{
    NSDictionary *param = @{@"letter":@"B"};
    callback(@[[NSNull null], param]);
}

在js端,通过NativeModules模块拿到Native中对应的模块

var manger = NativeModules.ExportMethodManger

如果RN中某个事件被触发了,直接通过manger向Native发送消息,消息名称正如你在Native中声明的那样

onPressLearnMore(){
        manger.exampleMethod("##############Learn More############")
    }

在Native项目中嵌入RN,你绝对不想要在js中再写一套网络请求吧。这时候就会遇到用RN展示UI,而数据请求放在在Native中做的情况。我们可以使用XMLHttpRequest这类js网络框架,但是这里想说的是async/await语法,它在ES6中被提供。从命名上一眼就能看出其目的,异步方法等待消息返回。

async updateEvents(){
        try {
            var events = await manger.findEvents();
            this.setState({
                letter:events[0].letter
            });
          } catch (e) {
            console.error(e);
          }
    }

这样,如果Native中findEvents是一个异步的方法就可以在js中等待Native的回调了。

从JS到Naitve所有的消息都是由NSInvocation负责转发,后面打算专门开一章讨论这个类在RN中的使用。

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

推荐阅读更多精彩内容