React native数据通信

作者: wenny826 | 来源:发表于2019-08-01 09:26 被阅读4次
    • 导航器传递参数给路由
    • 往导航栏传递参数
    • RN与原生模块通信

    导航器传递参数给路由

    示例:同导航器内的TestScreen1往TestScreen2传递了两个参数{params1:"1111",params2:"2222"}
    APP.js

    const taskNavigator = createStackNavigator({
        test1:TestScreen1,
        test2:TestScreen2
    })
    

    TestScreen1.js

     render() {
            return (
              <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>第一个界面!</Text>
                <Button onPress = { () =>{
                    this.props.navigation.navigate('test2',{params1:"1111",params2:"2222"})
                }}
                title = "进入第二个界面"/>
              </View>
            );
    

    TestScreen2.js

      render() {
            return (
              <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>第二个界面!</Text>
                <Text>{"params1 = " + this.props.navigation.state.params.params1}</Text>
                <Text>{"params2 = " + this.props.navigation.getParam("params2",2)}</Text>
                <Text>{"params3 = " + this.props.navigation.getParam("params3",3)}</Text>
              </View>
            );
          }
    

    获取时可用两种方式,
    第一种navigation.getParam(paramName,defaultValue),参数不存在时,取默认值
    第二种navigation.state.params. paramName, 如果未指定参数,它的值是 null。

    20190730_141515.gif

    往导航栏传递参数

    使用this.props.navigation.setParams({paramName1: paramValue1,paramName2: paramValue2,...})的方式向导航栏传值
    在导航栏中使用navigation.getParam(paramName,defaultValue)取值

    export default class TestScreen1 extends Component {
      _paramsValue = 0;
        static navigationOptions = ({ navigation }) => { 
          return {
          title: '第一个界面' + navigation.getParam("param",""),
          headerLayoutPreset: "center",
          headerTitleStyle: {
            fontWeight: 'normal',
            fontSize: 16,
            flex: 1,
            textAlign: 'center'
          },
          headerStyle: {
            backgroundColor: 'white',
            elevation: 5,
          },
        }};
          render() {
            return (
              <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>第一个界面!</Text>
                {/* <Button onPress = { () =>{
                    this.props.navigation.navigate('test2',{params1:"1111",params2:"2222"})
                }}
                title = "进入第二个界面"/> */}
                <Button onPress = { () =>{
                    this.props.navigation.setParams({param: ++this._paramsValue})
                }}
                title = "+1"/>
              </View>
            );
          }
        }
    
    20190730_152051.gif

    RN与原生模块通信

    一.RN 调用安卓代码

    经常会用到的场景比如获取一些安卓本地缓存数据等
    具体步骤如下:
    1.在Android端创建一个继承ReactContextBaseJavaModule的类,实现getName()方法,用来返回RN寻找到该类的名称

    public class ClientModule extends ReactContextBaseJavaModule {
    
    private ReactContext mReactContext;
    
    public ClientModule(ReactApplicationContext reactContext) {
      super(reactContext);
      mReactContext = reactContext;
    }
    
    @Override
    public String getName() {
      return "ClientModule";
    }
    }
    

    2.创建用于RN调用的方法,用@ReactMethod注解修饰

     @ReactMethod
      public void showToast(String message) {
        Toast.makeText(mReactContext,message,Toast.LENGTH_SHORT).show();
      }
    

    这里创建了一个弹出原生Toast的方法。

    3.创建一个实现ReactPackage接口的类,用来管理原生模块的Model,将继承了ReactContextBaseJavaModule的Model添加进去

    public class ClientPackage implements ReactPackage {
      @Nonnull
      @Override
      public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new ClientModule(reactContext));
        return modules;
      }
    
      @Nonnull
      @Override
      public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) {
        return Collections.emptyList();
      }
    }
    

    4.将创建好的ClientPackage添加到包管理列表中

    public class MainApplication extends Application implements ReactApplication {
    
      public static Context mContext;
    
      private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }
        @Override
        protected List<ReactPackage> getPackages() {
          return Arrays.<ReactPackage>asList(
              new MainReactPackage(),
              new RNGestureHandlerPackage(),
              new ClientPackage()
          );
        }
    
        @Override
        protected String getJSMainModuleName() {
          return "index";
        }
      };
    
      @Override
      public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
      }
    
      @Override
      public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
        mContext = getApplicationContext();
      }
    }
    

    5.在RN中调用
    导入组件

    import {NativeModules} from 'react-native';
    const clientModule = NativeModules.ClientModule;
    

    在需要弹出的地方调用

      <Button onPress = { () =>{
                    clientModule.showToast('这是一个原生Toast')
                }}
                title = "弹出原生Toast"/>
    
    20190730_164743.gif
    二. Promise 机制与安卓原生代码通信

    Promise 机制需要在原生代码中创建一个桥接方法,当方法的最后一个参数书一个Promise对象时,该方法会返回给js一个与之对应的Promise对象
    原生代码,在ClientModule中新增一个桥接方法

     @ReactMethod
     public void rnCallNativePromise(String message, Promise promise){
       Toast.makeText(mReactContext,message,Toast.LENGTH_SHORT).show();
       //得到组件名称
       String componentName = getCurrentActivity().getComponentName().toString();
       promise.resolve(componentName);
     }
    

    在js中调用

     <Button onPress = { () => {
                    clientModule.rnCallNativePromise('promise 弹出原生Toast')
                    .then((msg) => {
                      Alert.alert('promise 收到消息', msg)
                  }).catch((error) => {
                      console.log(error)
                }
              )}
             }
                title = "promise 弹出原生Toast"/>
              </View>
    
    20190730_172909.gif
    三.使用callback回调方式与安卓原生代码通信

    原生代码,在ClientModule中新增一个桥接方法,传入一个成功和失败的Callback回调

     @ReactMethod
      public void rnCallNativeCallback(Callback success, Callback error) {
        try {
          success.invoke(100, 200);
        } catch (Exception e) {
          error.invoke(e.getMessage());
        }
      }
    

    在js端调用

              <Button
                onPress={() => {
                  clientModule.rnCallNativeCallback((x, y) => {
                    Alert.alert('callback 收到消息', x + ',' + y)
                  }, (error) => {
                    console.log(error)
                  })
                }
                }
                title="rn CallBack" />
    
    20190730_174652.gif
    四.使用RCTDeviceEventEmitter与原生通信

    RCTDeviceEventEmitter类似一个EventBus消息传递机制,有发送方和接收方,与EventBus类似,在接收方需要注册监听,并在生命周期结束后取消监听

    1.通过ReactApplicationContextgetJSModule方法获取到RCTDeviceEventEmitter, 调用emit(eventName, paramss)方法发送一个消息事件

    因并不是原生代码所有的地方都有reactContext,所有建立一个工具类,在rn的model被加载后给工具类的静态变量reactContext赋值

    ClientModule:

     public ClientModule(@Nonnull ReactApplicationContext reactContext) {
        super(reactContext);
        mReactContext = reactContext;
        ToReactNativtUtil.reactContext = reactContext;
      }
    

    ToReactNativtUtil:

    public class ToReactNativtUtil {
      public static ReactApplicationContext reactContext;
      public static void sendEvent(String eventName, @Nullable WritableMap paramss) {
        if(reactContext == null)return;
        reactContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(eventName, paramss);
      }
    }
    

    需要发送消息的地方
    这里是直接在MainActivity启动后延时2s给RN发送一个登陆成功的事件,并传递一个userName参数

      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        UiInstance.getInstance().postRunnableDelayedToHandler(new Runnable() {
          @Override
          public void run() {
           //给RN发送一个loginSucc的事件
            WritableMap writableMap= Arguments.createMap();
            writableMap.putString("userName","wenny");
            ToReactNativtUtil.sendEvent("loginSucc",writableMap);
          }
        },2000);
       
      }
    

    2.在RN中通过DeviceEventEmitter来监听事件

    import { DeviceEventEmitter } from 'react-native';
    
      componentDidMount() {
        DeviceEventEmitter.addListener('loginSucc', (writableMap) => {
          Alert.alert('接收到 loginSucc userName :', writableMap.userName)
        });
      }
      componentWillUnmount() {
    //移除监听
        this.subscription.remove();
      }
    
    20190730_182814.gif
    五.利用getLaunchOptions方法向js传递一个Bundle类型的数据

    这种传递方式适用场景有:A(原生Activity)进入B(ReactActivity)时需要将A中的某些参数传递给B的js组件中。
    原生端发送:

      @Override
      protected ReactActivityDelegate createReactActivityDelegate() {
        return new ReactActivityDelegate(this, getMainComponentName()) {
          @Override
          protected ReactRootView createRootView() {
            return new RNGestureHandlerEnabledRootView(MainActivity.this);
          }
          @Nullable
          @Override
          protected Bundle getLaunchOptions() {
            Bundle bundle = new Bundle();
            bundle.putString("newsItemId","123");
            bundle.putString("newsItemName","新闻标题1");
            return bundle;
          }
        };
      }
    

    在js端的入口组件中获取,并通过导航器传参给路由
    App.js

    class App extends Component {
      appContainer
      constructor(props) {
        super(props);
        var newsItemId = this.props.newsItemId;
        var newsItemName = this.props.newsItemName;
    
        let stack = createStackNavigator({
          test1: TestScreen1,
          test2: TestScreen2
        }, {
            initialRouteParams: {
              newsItemId: newsItemId,
              newsItemName: newsItemName
            },
          })
          this.appContainer = createAppContainer(stack)
      }
      render() {
        return <this.appContainer />;
      }
    }
    AppRegistry.registerComponent("VideoProject", () => App);
    

    TestScreen1.js

    <Text >{navigation.getParam("newsItemId","-") +' ' + navigation.getParam("newsItemName","-")}</Text>
    

    相关文章

      网友评论

        本文标题:React native数据通信

        本文链接:https://www.haomeiwen.com/subject/suevrctx.html