首页 > 解决方案 > 无法将 Android Native Module 添加到 React Native 项目

问题描述

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

我遵循了这个 [tutorial][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();
      }
    }
  }
}

这是我使用本机模块的 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,
  });

添加所有内容后,我通过清理 Android 项目./gradlew clean并运行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. 当我通过在设备上运行 Android 应用程序Expo时,当应用程序启动时我有这样的日志:

` 错误:无法从 /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 层并查看更改,但我的原生模块仍然不存在。这可能是问题的根本原因吗?

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

标签: androidreact-nativereact-native-androidreact-native-native-modulereact-native-modules

解决方案


你的好像有问题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();
      }
    }
  }
}


推荐阅读