1. 程式人生 > 程式設計 >React Native JSI實現RN與原生通訊的示例程式碼

React Native JSI實現RN與原生通訊的示例程式碼

目錄
  • 什麼是I
  • JSI有什麼不同
  • 在iOS中使用JSI
    • iOS端配置
    • RN端配置
    • js呼叫帶引數的原生方法
    • 原生呼叫JS
    • 原生呼叫帶引數的JS方法
    • 在原生端呼叫js的函式引數
  • 總結
    • 問題
  • 參考資料

    什麼是JSI

    React Native JSI ( Interface) 可以使 Script 和 原生模組 更快、更簡單的通訊。它也是React Native 新的架構體系中Fabric UI層 和 Turbo 模組的核心部分。

    JSI有什麼不同

    JSI 移除了原生程式碼和JavaScript程式碼之間的橋接(bridge),同時也省去了兩端相互呼叫時大量的JSON序列化和反序列化操作。JSI為原生和JS互動打開了新的大門。下面是一些JSI的特點:

      http://www.cppcns.com
    1. JavaScript Interface 允許我們向JavaScript 執行時註冊方法。這些方法在js環境中可以通過 global物件獲取並呼叫。
    2. 我們完全可以使用C++或者在iOS裡使用OC ,在裡使用Java實現這些註冊方法。
    3. 原先使用bridge 的方式實現的原生模組可以通過增加一層C++,快速轉化為通過JSI實現。
    4. 在iOS端實現非常簡單,因為C++和OC 可以方便的實現混編。
    5. 在Android中,我們需要通過JNI 做一些轉化。
    6. 這些方法可以是完全同步的,這意味著不必強制使用async。await。

    在iOS中使用JSI

    下面我們將一步一步的在iOS工程中使用JSI實現原生與JS的通訊。
    建立一個新的React Native 專案

    npx react-native init jsiDemo
    

    iOS端配置

    在iOS專案目錄中建立C++檔案,example.h、 example.cpp。
    example.h

    #ifndef EXAMPLE_H
    #define EXAMPLE_H
    
    namespace facebook {
     namespace jsi {
      class Runtime;
     }
    }
    
    namespace example {
     void install(facebook::jsi::Runtime &jsiRuntime);
    }
    #endif /* EXAMPLE_H */
    
    example.m
    #include "example.h"
    #include <jsi/jsi.h>
    using namespace facebook::jsi;
    using namespace std;
    
    namespace example {
     void install(Runtime &jsiRuntime) {  
        auto helloWorld = Function::createFromHostFunction(jsiRuntime,PropNameID::forAscii(jsiRuntime,"helloWorld"),
    0,[](Runtime &runtime,const Value &thisValue,const Value *arguments,size_t count) -> Value { string helloworld = "helloworld"; return Value(runtime,String::createFromUtf8(runtime,helloworld)); }); jsiRuntime.global().setProperty(jsiRuntime,"helloWorld",move(helloWorld)); } }

    在上面的程式碼中,我們使用 createFromHostFunction 方法建立了一個方法,並通過setProperty 方法將其註冊到js執行時。
    接下來,我們需要建立一個moudle,在moudle中執行install 方法。
    我們建立OC 檔案,SimpleJsi.h , SimpleJsi.mm

    SimpleJsi.h

    #import <React/RCTBridgeModule.h>
    @interface SimpleJsi : NSObject <RCTBridgeModule>
    @property (nonatomic,assign) BOOL setBridgeOnMainQueue;
    @end
    

    SimpleJsi.mm

    #import "SimpleJsi.h"
    #import <React/RCTBridge+Private.h>
    #import <React/RCTUtils.h>
    #import <jsi/jsi.h>
    #import "example.h"
    #import <sys/utsname.h>
    
    using namespace facebook::jsi;
    using namespace std;
    
    @implementation SimpleJsi
    
    @synthesize bridge = _bridge;
    @synthesize methodQueue = _methodQueue;
    
    RCT_EXPORT_MODULE()
    
    + (BOOL)requiresMainQueueSetup {
        
        return YES;
    }
    
    - (void)setBridge:(RCTBridge *)bridge {
        _bridge = bridge;
        _setBridgeOnMainQueue = RCTIsMainQueue();
        [self installLibrary];
    }
    
    - (void)installLibrary {
        
        RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
        
        if (!cxxBridge.runtime) {
            
            /**
             * This is a workaround to install library
             * as soon as runtime becomes available and is
             * not recommended. If you see random crashes in iOS
             * global.xxx not found etc. use this.
             */
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,0.001 * NSEC_PER_SEC),dispatch_get_main_queue(),^{
                /**
                 When refreshing the app while debugging,the setBridge
                 method is called too soon. The runtime is not ready yet
                 quite often. We need to install library as soon as runtime
                 becomes available.
                 */
                [self installLibrary];
                
            });
            return;
        }
        
        example::install(*(facebook::jsi::Run客棧time *)cxxBridge.runtime);
    }
    
    @end

    在 setBridge 方法中,我們呼叫了 example的 install 方法,完成方法的註冊。

    RN端配置

    修改App.js

    import React from 'react';
    import type {Node} from 'react';
    import {Text,View,Button} from 'react-native';
    
    const App: () => Node = () => {
      const [result,setResult] = React.useState();
    
      const press = () => {
        setResult(global.helloWorld());
      };
      return (
        // eslint-disable-next-line react-native/no-inline-styles
        <View style={{backgroundColor: '#FFFFFF',height: '100%'}}>
          <View style={{height: '10%'}} />
          <Button onPress={press} title="按鈕" />
          <Text>{'呼叫helloword:' + result}</Text>
        </View>
      );
    };
    
    export default App;
    
    

    點選按鈕之後,發現result的值為helloworld。

    結果

    React Native JSI實現RN與原生通訊的示例程式碼

    上面我們實現了js 呼叫原生,但沒有引數,接下來我們實現一個單引數的呼叫。

    js呼叫帶引數的原生方法

    我們在example.cpp 的 install 方法中增加multiply方法的註冊,從arguments 中取出入參。

    auto multiply = Function::createFromHostFunction(jsiRuntime,       "multiply"),2,size_t count) -> Value {
            int x = arguments[0].getNumber();
            int y = arguments[1].getNumber();
            return Value(x * y); 
        });
    

    jsiRuntime.global().setProperty(jsiRuntime,"multiply",move(multiply));

    然後修改App.js

    import React from 'react';
    import type {Node} from 'react';
    import {Text,setResult] = React.useState();
    
      const press = () => {
        setResult(global.multiply(2,2));
      };
      return (
        // eslint-disable-next-line react-native/no-inline-styles
        <View style={{backgroundColor: '#FFFFFF',height: '100%'}}>
          <View style={{height: '10%'}} />
          <Button onPress={press} title="按鈕" />
          <Text>{'2*2 = ' + result}</Text>
        </View>
      );
    };
    
    export default App;

    結果

    React Native JSI實現RN與原生通訊的示例程式碼

    原生呼叫JS

    原生呼叫js主要通過jsiRuntime.global().getPropertyAsFunction(jsiRuntime,"jsMethod").call(jsiRuntime);方法實現。
    首先我們在js中增加一個js方法。我們修改App.js 在上面增加jsMethod 方法。

    import React from 'react';
    import type {Node} from 'react';
    import {Text,setResult] = React.useState();
    
      global.jsMethod = () => {
        alert('hello jsMethod');
      };
    
      const press = () => {
        setResult(global.multiply(2,height: '100%'}}>
          <View style={{height: '10%'}} />
          <Button onPress={press} title="按鈕" />
          <Text>{'2*2 = ' + result}</Text>
        </View>
      );
    };
    
    export default App;

    在原生端,我們假設在進入應用的時候觸發呼叫js方法,我們修改AppDelegate中程式設計客棧的applicationWillEnterForeground 方法。

    - (void)applicationWillEnterForeground:(UIApplication *)application {
      SimpleJsi *jsi = [self.bridge moduleForName:@"SimpleJsi"];
      [jsi calljs];
    }
    

    通過moduleForName方法獲取SimpleJsi物件, 然後通過SimpleJsi中的calljs 方法。

    - (void)calljs {
      RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
      Runtime &jsiRuntime = *(facebook::jsi::Runtime *)cxxBridge.runtime;
      jsiRuntime.global().getPropertyAsFunction(jsiRuntime,"jsMethod").call(jsiRuntime);
    }
    

    結果

    React Native JSI實現RN與原生通訊的示例程式碼

    原生呼叫帶引數的JS方法

    多引數呼叫和無參呼叫類似,只是在call方法後面增加了引數列表。
    首先我們先在js側定義方法,修改app.js

    import React from 'react';
    import type {Node} from 'react';
    import {Text,setResult] = React.useState();
    
      global.jsMethod = () => {
        alert('hello jsMethod');
      };
    
      global.jsMultiply = (x,y) => {
        alert('x * y = ' + x * y);
      };
    
      const press = () => {
        setResult(global.multiply(2,height: '100%'}}>
          <View style={{height: '10%'}} />
          <Button onPress={press} title="按鈕" />
          <Text>{'2*2 = ' + result}</Text>
        </View>
      );
    };
    
    export default App;
    
    

    然後我們修改原生端的呼叫
    AppDelegate.m

    - (void)applicationWillEnterForeground:(UIApplication *)application {
      SimpleJsi *jsi =  [self.bridge moduleForName:@"SimpleJsi"];
    //  [jsi calljs];
      [jsi callJsMultiply:4 y:4];
    }
    

    SimpleJsi.m

    - (void)callJsMultiply:(int)x y:(int) y {
      RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
      Runtime &jsiRuntime = *(facebook::jsi::Runtime *)cxxBridge.runtime;
      jsiRuntime.global().getPropertyAsFunction(jsiRuntime,"jsMultiply").call(jsiRuntime,x,y);
    }
    

    結果

    React Native JSI實現RN與原生通訊的示例程式碼

    在原生端呼叫js的函式引數

    在原生中呼叫js引數中的函式,需要通過arguments[i].getObject(runtime).getFunction(runtime).call(runtime,value);的方式觸發。

    首先我們在example.cpp 中新註冊一個方法multiplyWithCallback

    auto multiplyWithCallback = Function::createFromHostFunction(jsiRuntime,"multiplyWithCallback"),3,size_t count) -> Value {
            int x = arguments[0].getNumber();
            int y = arguments[1].getNumber();
            //呼叫callback
            arguments[2].getObject(runtime).getFunction(runtime).call(runtime,x * y);
            
            return Value();
            
        });
        
        jsiRuntime.global().setProperty(jsiRuntime,"multiplyWithCallback",move(multiplyWithCallback));
    

    在js側進行呼叫, 修改app.js

    import React from 'react';
    import type {Node} from 'react';
    import {Text,y) => {
        alert('x * y = ' + x * y);
      };
    
      const press = () => {
        // setResult(global.multiply(2,2));
        global.multiplyWithCallback(4,5,alertResult);
      };
    
      const alertResult = res => {
        alert(res);
      };
    
      return (
        // eslint-disable-next-line react-native/no-inline-styles
        <View style={{backgroundColor: '#FFFFFF',height: '100%'}}>
          <View style={{height: '10%'}} />
          <Button onPress={press} title="按鈕" />
          <Text>{'2*2 = ' + result}</Text>
        </View>
      );
    };
    
    export default App;
    
    

    點選按鈕之後,會呼叫multiplyWithCallback 將alertResult 方式傳遞給原生。

    結果

    React Native JSI實現RN與原生通訊的示例程式碼

    總結

    上面就是本文對JSI 的介紹,文中的程式碼,你可以在公眾號回覆 JSI 獲取 下載地址。

    問題

    在RN Debug的情況下,global.xx 無法找到對應的方法,個人也沒有頭緒,如果你有方法解決,請聯絡我,非常感謝。

    參考資料

    https://blog.notesnook.com/getting-started-react-native-jsi/
    reactnative.maxieewong.com/
    https://github.com/react-native-community/discussions-and-proposals/issues/91
    ospfranco.com/

    到此這篇關於React Native JSI實現RN與原生通訊的示例程式碼的文章就介紹到這了,更多相關React Native原生通訊內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!