我的 React Native 技能树点亮计划 & 基于 JavaScriptModule 实现 Native 调用 Javascript 方法

作者简介:ASCE1885, 《Android 高级进阶》作者。

  • 微信公众号:asce1885
  • 小密圈:Android高级进阶,详情见这篇文章。
  • Github,,微博,CSDN 知乎
    本文由于潜在的商业目的,不开放全文转载许可,谢谢!
我的 React Native 技能树点亮计划 & 基于 JavaScriptModule 实现 Native 调用 Javascript 方法_第1张图片
white horse.png

在 React Native 开发中,某些情况下存在需要从 Native 端直接调用 Javascript 代码中某个方法的需求,这时候我们就需要用到 JavaScriptModule 这个接口,如下所示:

/**
 * Interface denoting that a class is the interface to a module with the same name in JS. Calling
 * functions on this interface will result in corresponding methods in JS being called.
 *
 * When extending JavaScriptModule and registering it with a CatalystInstance, all public methods
 * are assumed to be implemented on a JS module with the same name as this class. Calling methods
 * on the object returned from {@link ReactContext#getJSModule} or
 * {@link CatalystInstance#getJSModule} will result in the methods with those names exported by
 * that module being called in JS.
 *
 * NB: JavaScriptModule does not allow method name overloading because JS does not allow method name
 * overloading.
 */
@DoNotStrip
public interface JavaScriptModule {
}

本文以 Android 平台为例进行说明,从 JavaScriptModule 的头注释中我们可以看出:

  • 一个接口如果继承了 JavaScriptModule 接口,并按照后面我们即将说到的步骤进行配置,那么就可以实现这个 Native 接口到 Javascript 中同名模块的映射
  • 这个 Native 接口中所有的公用方法也将一一映射到同名的 Javascript 模块中的方法,调用 Native 接口的方法相当于直接调用到同名 Javascript 模块中的方法
  • 由于 Javascript 不支持方法名重载,因此 Native 端继承 JavaScriptModule 的接口中不能存在重载的方法

那么如何配置才能实现上面说到的功能呢?总的来说,可以分为以下几个步骤:

实现 JavaScriptModule

新增一个 Native 接口,继承 JavaScriptModule 接口,并声明所需的方法,如下所示,我们声明一个 init 方法:

public interface AppModuleInitializer extends JavaScriptModule {
    void init(String name);
}

接着在工程的自定义的 ReactPackage 类的 createJSModules 方法中将这个 Native 接口添加到 React Native 框架的 JavaScriptModule 列表中,如下所示(假设工程中的 ReactPackage 类名为 AppReactPackage,并继承自 React Native 框架的 MainReactPackage 类,已省略无关的代码):

public class AppReactPackage extends MainReactPackage {

    @Override
    public List> createJSModules() {
        List> javaScriptModules = new ArrayList<>();
        javaScriptModules.addAll(super.createJSModules());
        javaScriptModules.add(AppModuleInitializer.class);
        return javaScriptModules;
    }
}

注册 AppReactPackage

上面说到的 AppReactPackage 记得注册到应用中,有两种方法,一种是在应用的 Application 中通过实现 ReactNativeHostgetPackages 方法,如下所示(通过 react-native init 生成的官方 demo):

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List getPackages() {
      return Arrays.asList(
          new AppReactPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}

另一种方法是直接通过 ReactInstanceManager 类来进行注册,如下代码片段所示:

ReactInstanceManager.Builder builder = ReactInstanceManager.builder().setApplication(application)
                .setJSBundleFile(bundleUrl)
                .setJSMainModuleName("index")
                .addPackage(new AppReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.BEFORE_RESUME);

创建并注册同名的 Javascript 模块

在 Javascript 目录中我们创建名为 AppModuleInitializer.js 的文件,并在其中定义跟 Native 同名的 init 方法,如下所示:

class AppModuleInitializer {
    init(json: string) {
        // do something
    }
}

AppModuleInitializer = new AppModuleInitializer();

// 注册
BatchedBridge.registerCallableModule(
    'AppModuleInitializer',
    AppModuleInitializer);

module.exports = AppModuleInitializer;

开始使用

在 React Native 上下文初始化完成后,我们就可以在代码中调用上面注册的方法了,调用方式如下所示:

AppModuleInitializer initModule = context.getJSModule(AppModuleInitializer.class);
initModule.init("ASCE1885");

源码中的例子

在 React Native 源码中也存在不少对 JavaScriptModule 使用的例子,我们直接在源码中搜索对 JavaScriptModule 的使用,可以看到如下图所示,对这个接口的继承或者实现有三十多处:

我的 React Native 技能树点亮计划 & 基于 JavaScriptModule 实现 Native 调用 Javascript 方法_第2张图片
MacHi 2017-05-22 19-18-08.png

例如其中的 HMRClientAppRegistry 的 Native 端定义如下,感兴趣的读者可以自己按着上面介绍的步骤来分析下这些源码的实现。

/**
 * JS module interface for HMRClient
 *
 * The HMR(Hot Module Replacement)Client allows for the application to receive updates
 * from the packager server (over a web socket), allowing for injection of JavaScript to
 * the running application (without a refresh).
 */
public interface HMRClient extends JavaScriptModule {

  /**
   * Enable the HMRClient so that the client will receive updates
   * from the packager server.
   * @param platform The platform in which HMR updates will be enabled. Should be "android".
   * @param bundleEntry The path to the bundle entry file (e.g. index.ios.bundle).
   * @param host The host that the HMRClient should communicate with.
   * @param port The port that the HMRClient should communicate with on the host.
   */
  void enable(String platform, String bundleEntry, String host, int port);
}


/**
 * JS module interface - main entry point for launching React application for a given key.
 */
public interface AppRegistry extends JavaScriptModule {

  void runApplication(String appKey, WritableMap appParameters);
  void unmountApplicationComponentAtRootTag(int rootNodeTag);
  void startHeadlessTask(int taskId, String taskKey, WritableMap data);
}

你可能感兴趣的:(我的 React Native 技能树点亮计划 & 基于 JavaScriptModule 实现 Native 调用 Javascript 方法)