Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【RN】使用NativeModules获取设备信息(Android&iOS) #40

Open
fayeah opened this issue Oct 18, 2020 · 0 comments
Open

【RN】使用NativeModules获取设备信息(Android&iOS) #40

fayeah opened this issue Oct 18, 2020 · 0 comments

Comments

@fayeah
Copy link
Owner

fayeah commented Oct 18, 2020

Why Native Modules

在使用React Native开发手机应用的时候,有时候我们需要使用到platform API,或者写一些多线程的,提高性能的代码,又或者我们需要重新实现一个Native的API,那么React Native提供了这样一个feature来支持我们做这些事情。这次我们以获取设备信息为例来看看如何使用Native Modules实现。

Pre-requisite:

  • 一个React Native的工程
  • 能正常使用Android或是iOS simulator启动运行

Android

  1. 在项目工程里面我们能看到android的目录,使用Android Studio打开,建议使用Android Studio打开,会自动刷新我们需要的依赖。

  2. 创建一个Java文 DeviceModule.javamobileNew/android/app/src/main/java/com/mobilenew/device/DeviceModule.java,代码如下

    package com.mobilenew.device;
    
    import com.facebook.react.bridge.Callback;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    
    public class DeviceModule extends ReactContextBaseJavaModule {
        //constructor
        public DeviceModule(ReactApplicationContext reactContext) {
            super(reactContext);
        }
        //Mandatory function getName that specifies the module name
        @Override
        public String getName() {
            return "Device";
        }
        //Custom function that we are going to export to JS
        @ReactMethod
        public void getDeviceName(Callback cb) {
            try{
                cb.invoke(null, android.os.Build.MODEL);
            }catch (Exception e){
                cb.invoke(e.toString(), null);
            }
        }
    }
    • 自定义的native类需要继承ReactContextBaseJavaModule
    • 我们需要重写getName()方法,这个方法的返回值是react导入所需要的module名字
    • 实现一个function,加上@ReactMethod注解,这样该方法就能正常导出
  3. 创建了自定义module之后,我们需要注册该module,创建一个Java类 DevicePackage/mobileNew/android/app/src/main/java/com/mobilenew/device/DevicePackage.java,代码如下:

    package com.mobilenew.device;
    
    import com.facebook.react.ReactPackage;
    import com.facebook.react.bridge.JavaScriptModule;
    import com.facebook.react.bridge.NativeModule;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.uimanager.ViewManager;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class DevicePackage implements ReactPackage {
    
        @Override
        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Collections.emptyList();
        }
    
        @Override
        public List<NativeModule> createNativeModules(
                ReactApplicationContext reactContext) {
            List<NativeModule> modules = new ArrayList<>();
            //We import the module file here
            modules.add(new DeviceModule(reactContext));
    
            return modules;
        }
    
        // Backward compatibility
        public List<Class<? extends JavaScriptModule>> createJSModules() {
            return new ArrayList<>();
        }
    }
  4. 注册好module之后,我们需要将 DevicePackage进行初始化,在MainApplication.java文件里面,添加如下代码:

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          // 这里是 要添加的DevicePackage
           packages.add(new DevicePackage());
          return packages;
        }

至此,Android的配置已经完成,我们iOS的配置完成之后,再看react native端如何测试验证,RN端的代码是一致的。

iOS

  1. 在项目工程里面我们能看到ios的目录,使用xcode打开后缀为.xcodeproj的文件,看到工程目录

  2. 可以创建一个group,方便更好地组织代码结构,我这里创建了一个device的group

  3. 在该group下面创建device的声明文件Device.h

    image

    声明文件代码:

    #import <React/RCTBridgeModule.h>
    
    @interface Device : NSObject <RCTBridgeModule>
    @end
  4. 类似地,创建实现文件Device.m

    #import "Device.h"
    #import <UIKit/UIKit.h>
    
    @implementation Device
    
    //export the name of the native module as 'Device' since no explicit name is mentioned
    RCT_EXPORT_MODULE();
    
    //exports a method getDeviceName to javascript
    RCT_EXPORT_METHOD(getDeviceName:(RCTResponseSenderBlock)callback){
    @try{
      NSString *deviceName = [[UIDevice currentDevice] name];
      callback(@[[NSNull null], deviceName]);
    }
    @catch(NSException *exception){
      callback(@[exception.reason, [NSNull null]]);
    }
    }
    @end
    • RCT_EXPORT_MODULE()是一个宏方法,用来定义要导出的module的名字
    • RCT_EXPORT_METHOD 用来表示要导出的方法的名字,从而是的javascript端可以调用
  5. iOS要注意的一点是,如果使用了自定义module,在开发环境会有一个warning:

    RCTBridge required dispatch_sync to load RCTDevLoadingView. This may lead to deadlocks
    

    解决的办法是在AppDelegate.m文件里面添加如下代码:

    #if RCT_DEV
    #import <React/RCTDevLoadingView.h>
    #endif
     .......
    
    RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
    #if RCT_DEV
      [bridge moduleForClass:[RCTDevLoadingView class]];
    #endif

React Native端调用自定义Module

  1. native 的自定义module实现完成之后,在reactive native的代码里进行测试验证,比如我们点击一个按钮之后,想要弹出设备信息:
    import React from 'react';
    import {
      StyleSheet,
      Text,
      TouchableOpacity,
      View,
      NativeModules,
      Alert,
    } from 'react-native';
    
    const HomeScreen = () => {
      handleHelpPress = () => {
        //  测试自定义module的方法
        const config = NativeModules.Device || {getDeviceName: (f) => f()};
        config.getDeviceName((error, name) => {
          Alert.alert('Device', name);
        });
      };
      return (
        <View style={styles.helpContainer}>
          <TouchableOpacity onPress={this.handleHelpPress} style={styles.helpLink}>
            <Text style={styles.helpLinkText}>get Device from Native Modules</Text>
          </TouchableOpacity>
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      helpContainer: {
        marginTop: 15,
        alignItems: 'center',
      },
      helpLinkText: {
        fontSize: 14,
        color: '#2e78b7',
      },
    });
    
    export default HomeScreen;
  2. 效果如下:
    image
    image

Reference:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant