无法将 Android Native Module 添加到 React Native 项目

问题描述

我有一项任务,需要为 Android 设备的 React Native 项目添加 Native 功能

我遵循了这个 [教程][1],它非常简单明了。但不幸的是,我仍然无法从 JS 层访问我的 Native 功能

这是我的代码的样子。

SimpleTestModule.java

package com.reactnativesampleapp;

import android.util.Log;

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;

public class SimpleTestModule extends ReactContextBaseJavaModule {

    SimpleTestModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "SimpleTestModule";
    }

    @ReactMethod
    public void simplePublicmethod() {
        Log.d("SimpleTestModule","Some cool log!");
    }
}

SimpleTestPackage.java

package com.reactnativesampleapp;

import android.util.Log;

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 SimpleTestPackage 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 SimpleTestModule(reactContext));
        Log.d("SimpleTestPackage","Log just to check if this code gets executed");

        return modules;
    }
}

最后,这是我的MainApplication.java

package com.reactnativesampleapp;

import android.app.Application;
import android.content.Context;
import android.net.Uri;
import android.util.Log;

import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.soLoader;
import com.makeupreactnativesampleapp.generated.BasePackageList;

import org.unimodules.adapters.react.ReactAdapterPackage;
import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
import org.unimodules.core.interfaces.Package;
import org.unimodules.core.interfaces.SingletonModule;
import expo.modules.constants.ConstantsPackage;
import expo.modules.permissions.PermissionsPackage;
import expo.modules.filesystem.FileSystemPackage;
import expo.modules.updates.UpdatesController;

import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;

public class MainApplication extends Application implements ReactApplication {
  private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
    new BasePackageList().getPackageList()
  );

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

    @Override
    protected List<ReactPackage> getPackages() {
      Log.d("Eugene","getPackages!");
      List<ReactPackage> packages = new PackageList(this).getPackages();
      packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider));
      packages.add(new SimpleTestPackage());
      return packages;
    }

    @Override
    protected String getJSMainModuleName() {
      return "index";
    }

    @Override
    protected JSIModulePackage getJSIModulePackage() {
      return new ReanimatedJSIModulePackage();
    }

    @Override
    protected @Nullable String getJSBundleFile() {
      if (BuildConfig.DEBUG) {
        return super.getJSBundleFile();
      } else {
        return UpdatesController.getInstance().getLaunchAssetFile();
      }
    }

    @Override
    protected @Nullable String getBundleAssetName() {
      if (BuildConfig.DEBUG) {
        return super.getBundleAssetName();
      } else {
        return UpdatesController.getInstance().getBundleAssetName();
      }
    }
  };

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

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

    if (!BuildConfig.DEBUG) {
      UpdatesController.initialize(this);
    }

    initializeflipper(this,getReactNativeHost().getReactInstanceManager());
  }

  /**
   * Loads Flipper in React Native templates. Call this in the onCreate method with something like
   * initializeflipper(this,getReactNativeHost().getReactInstanceManager());
   *
   * @param context
   * @param reactInstanceManager
   */
  private static void initializeflipper(
      Context context,ReactInstanceManager reactInstanceManager) {
    if (BuildConfig.DEBUG) {
      try {
        /*
         We use reflection here to pick up the class that initializes Flipper,since Flipper library is not available in release mode
        */
        Class<?> aClass = Class.forName("com.makeupreactnativesampleapp.ReactNativeFlipper");
        aClass
            .getmethod("initializeflipper",Context.class,ReactInstanceManager.class)
            .invoke(null,context,reactInstanceManager);
      } catch (ClassNotFoundException e) {
        e.printstacktrace();
      } catch (NoSuchMethodException e) {
        e.printstacktrace();
      } catch (illegalaccessexception e) {
        e.printstacktrace();
      } catch (InvocationTargetException e) {
        e.printstacktrace();
      }
    }
  }
}

这是我使用 Native 模块的 JS 文件

import React from 'react';

import { 
  StyleSheet,SafeAreaView,NativeModules } from 'react-native';

const { SimpleTestModule } = NativeModules;

export default function App() {

  SimpleTestModule.simplePublicmethod();
  return (
    <SafeAreaView style={styles.container}>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,flexDirection: "column",backgroundColor: "#fff",justifyContent: "space-around",alignItems: "center",paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0,});

添加所有内容后,我通过 ./gradlew clean 清理 Android 项目并运行 npm run android

一些注意事项:

  1. 我已经在我的 Package 类中添加一个日志,只是为了查看它是否被执行。不幸的是,日志从未出现过,所以我想它不会被执行
  2. 当我运行 npm run android 时,编译成功,但 .apk 无法在设备上自动启动。这是否表明我的设置存在严重问题?

这是 npm run android 执行结束时的日志:

BUILD SUCCESSFUL in 49s
508 actionable tasks: 508 executed
info Connecting to the development server...
warn Failed to connect to development server using "adb reverse": spawnSync adb ENOENT
info Starting the app...
error Failed to start the app. Run CLI with --verbose flag for more details.
Error: spawnSync adb ENOENT
    at Object.spawnSync (node:internal/child_process:1086:20)

  [1]: https://reactnative.dev/docs/native-modules-android
  1. 当我通过 Expo 在设备上运行 Android 应用程序时,当应用程序启动时我有这样的日志:

` 错误:无法从 /Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/. 解析模块 ./debugger-ui/debuggerWorker.aca173c4:

None of these files exist:
  * debugger-ui/debuggerWorker.aca173c4(.native|.native.ts|.ts|.native.tsx|.tsx|.native.js|.js|.native.jsx|.jsx|.native.json|.json)
  * debugger-ui/debuggerWorker.aca173c4/index(.native|.native.ts|.ts|.native.tsx|.tsx|.native.js|.js|.native.jsx|.jsx|.native.json|.json)
    at ModuleResolver.resolveDependency (/Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:168:15)
    at DependencyGraph.resolveDependency (/Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/node_modules/metro/src/node-haste/DependencyGraph.js:353:43)
    at /Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/node_modules/metro/src/lib/transformHelpers.js:271:42
    at /Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/node_modules/metro/src/Server.js:1097:37
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/node_modules/metro/src/Server.js:99:24)
    at _next (/Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/node_modules/metro/src/Server.js:119:9)
    at processticksAndRejections (node:internal/process/task_queues:96:5)
Error: Unable to resolve module ./debugger-ui/debuggerWorker.aca173c4 from /Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/.:`

此消息重复多次(实际上是 7 次),但该应用正在运行。我仍然可以成功修改 JS 层并看到更改,但我的本机模块仍然不存在。这会是问题的根本原因吗?

任何帮助将不胜感激。我试图尽可能准确,但如果您需要任何其他信息以分享专业知识,请告诉我。提前致谢!

解决方法

您的 MainApplication.java 似乎有问题。我试过你的文件,它有一些未定义的参数。
如果它适合你,试试这个。

package com.reactnativesampleapp;

import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

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

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          packages.add(new SimpleTestPackage());
          // Packages that cannot be autolinked yet can be added manually here,for example:
          // packages.add(new MyReactNativePackage());
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          return "index";
        }
      };

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

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

  /**
   * Loads Flipper in React Native templates. Call this in the onCreate method with something like
   * initializeFlipper(this,getReactNativeHost().getReactInstanceManager());
   *
   * @param context
   * @param reactInstanceManager
   */
  private static void initializeFlipper(
      Context context,ReactInstanceManager reactInstanceManager) {
    if (BuildConfig.DEBUG) {
      try {
        /*
         We use reflection here to pick up the class that initializes Flipper,since Flipper library is not available in release mode
        */
        Class<?> aClass = Class.forName("com.reactnativesampleapp.ReactNativeFlipper");
        aClass
            .getMethod("initializeFlipper",Context.class,ReactInstanceManager.class)
            .invoke(null,context,reactInstanceManager);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      }
    }
  }
}