在RN中实现Android原生模块和UI封装

650 阅读3分钟

在RN中实现Android原生模块和UI封装

一.封装原生Module

有时候现有的RN没有相关模块来访问一些android底层功能api,我们可以尝试封装一个android原生的模块,然后js端去调用模块的api实现。比如集成第三方的sdk,js层需要调用sdk api,我们就可以封装一个android模块集成sdk再给到js去调用。

1. 创建模块

新建一个java类,该类要继承 ReactContextBaseJavaModule

新建 RnModule.java文件
//RnModule.javapackage com.your-app-name;
import android.widget.Toast;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
​
import java.util.Map;
import java.util.HashMap;
​
public class RnModule extends ReactContextBaseJavaModule {
  private static ReactApplicationContext reactContext;
  public RnModule(ReactApplicationContext context) {
    super(context);
    reactContext = context;
  }
}

2. 实现getName方法

实现ReactContextBaseJavaModule类中的 getName方法,该方法的返回值作为js中引入原生模块的标识

package com.your-app-name;
import android.widget.Toast;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
​
import java.util.Map;
import java.util.HashMap;
​
public class RnModule extends ReactContextBaseJavaModule {
  private static ReactApplicationContext reactContext;
  public RnModule(ReactApplicationContext context) {
    super(context);
    reactContext = context;
  }
  **  
  @Override
  public String getName() {
    return "RnModule";
  }
  **
}

3. 注册模块

实现了模块后我们要注册才能生效

步骤:1.先将自定义的原生模块添加packge中去 2.然后将这个package提供出去

1. 新建一个java类,该类要实现ReactPackage接口,将上面定义的模块在Package类的createNativeModules方法add进去

新建 RnPackage.java 文件
// RnPackage.javapackage com.your-app-name;
​
import com.facebook.react.ReactPackage;
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 CustomToastPackage implements ReactPackage {
​
  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }
​
  @Override
  public List<NativeModule> createNativeModules(
                              ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();
​
    modules.add(new RnModule(reactContext));//将实现的module增加进去
​
    return modules;
  }
​
}

2.在MainApplication.java用getPackages方法将上面的package提供出去

protected List<ReactPackage> getPackages() {
  @SuppressWarnings("UnnecessaryLocalVariable")
  List<ReactPackage> packages = new PackageList(this).getPackages();
  // Packages that cannot be autolinked yet can be added manually here, for example:
  // packages.add(new MyReactNativePackage());
  packages.add(new RnPackage()); // <-- 添加这一行,类名替换成你的Package类的名字 name.
  return packages;
}

4. 在js层使用

import { NativeModules } from 'react-native';
export default NativeModules.RnModule;//RnMuduel为原生模块中getName()中返回的字符串

----------------到此已经完成原生模块的创建和js层引入使用,接下来在原生模块中添加方法-----------------

5.原生模块导出给js层调用方法

  • 导出给js层的方法,Java 方法需要使用注解@ReactMethod
  原生模块:
      @ReactMethod
      public void show(String message, int duration) {
​
      }
  
  js层:
       RnModule.show('你好'100)
    
  • 给js层返回参数,callback或者Promise
原生模块:
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
public class RnModule extends ReactContextBaseJavaModule {
​
...
  //callback方式
      @ReactMethod
      public void show(String message, int duration,Callback successCallback) {
            successCallback.invoke('成功')
      }
  //Promise 方式
      @ReactMethod
      public void show(String message, int duration,Promise) {
            Promise.resolve('成功')//Promise.reject(err)
      }
...
​
​
js层:
  //callback方式
    RnModule.show('你好'100,(msg)=>{console.log(msg)//成功})
  //Promise 方式
    RnModule.show('你好'100).then((msg)=>{console.log(msg)//成功}).catch((err)=>{})

6.原生模块发送事件给js层

在js层不调用原生模块方法下,如何往js层发送通知和数据

比如:原生模块有一些事件监听逻辑,只有在触发的时候才会调用,而js层需要知道该事件触发和相应的回调参数

通过使用ReactContext上的getJsModule方法获得RCTDeviceEventEmitter引用实现
​
原生模块:
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
public class RnModule extends ReactContextBaseJavaModule {
​
...
    private void sendEvent(String eventName,
                           @Nullable WritableMap params) {
      this.getReactApplicationContext()
          .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
          .emit(eventName, params);
    }
    //在对应的逻辑中触发事件
    sendEvent("event",params)// "event":为事件名称
                                "params":触发事件的回调参数
...
​
​
js层:js层去监听原生模块的触发的事件
        import { NativeEventEmitter, NativeModules } from 'react-native';
        const eventEmitter = new NativeEventEmitter(NativeModules.RnModule);
        this.eventListener=eventEmitter.addListener('event', (params) => {
            console.log(params)
        });
        //注意要销毁:this.eventListener.remove();

二.封装原生View

构建一个原生UI组件,给到js层使用

1.创建自定义的原生视图

比如实现一个ImageView图片展示组件

1.创建ReactImageView.java
2.ReactImageView类中自定义实现UI和逻辑
例如:
    public class MyFragment extends FrameLayout {
        public ReactImageView(ReactContext context){
            super(context)
            //相关逻辑
        }
        //接收js组件传过来的props参数,提供方法给ViewManager调用
        public void setSrc(String src){
            this.src=src
        }
    }

2.创建ViewManager子类

1.创建 ReactImageManager.java文件,
2.定义ReactImageManager类,继承SimpleViewManager<ReactImageView>,其中ReactImageView就是我们上面自定义的原生视图
3.实现getName方法
4.在createViewInstance中实例化创建这个视图
5.通过@ReactProp注解来接收,js调用组件时传过来的属性值(类似props方式)。然后在自定义的原生视图中有对应的方法接收参数
例如:
    public class ReactImageManager extends SimpleViewManager<ReactImageView> {
​
      public static final String REACT_CLASS = "RCTImageView";
      ReactApplicationContext mCallerContext;
​
      public ReactImageManager(ReactApplicationContext reactContext) {
        mCallerContext = reactContext;
      }
​
      @Override
      public String getName() {
        return REACT_CLASS;
      }
​
      @Override
      public ReactImageView createViewInstance(ThemedReactContext context) {
        return new ReactImageView(context);//实例化创建这个视图
      }
      @ReactProp(name = "src")
          public void setSrc(ReactImageView view, String src) {
            view.setSrc(src);//在自定义的原生视图中(ReactImageView.java)有对应的setSrc方法来获取参数
      }
​
    }

3.注册ViewManager

和模块注册一样的,唯一不同的是将UI模块在Package类的createViewManagers方法add进去

新建 RnPackage.java 文件
// RnPackage.javapackage com.your-app-name;
​
import com.facebook.react.ReactPackage;
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 CustomToastPackage implements ReactPackage {
​
  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {//注册UI
    List<ViewManager> managers=new ArrayList<>()
    managers.add(new ReactImageManager())
    return Collections.emptyList();
  }
​
  @Override
  public List<NativeModule> createNativeModules(
                              ReactApplicationContext reactContext) {//注册模块
    List<NativeModule> modules = new ArrayList<>();
​
    modules.add(new RnModule(reactContext));//将实现的module增加进去
​
    return modules;
  }
​
}

4.在js层使用

MyViewManager.jsx
    import { requireNativeComponent } from 'react-native';
    export default requireNativeComponent('RCTImageView')
    
    
MyView.js
    import RCTImageView from './MyViewManager'
    
    const MyView=()=>{
        return (
            <RCTImageView
                style={{}}
                src="htpps://www.baidu.com"
            />
        )
    }

\