I have a task where I need to add a Native functionality to the React Native project for Android devices.
I have followed this [tutorial][1], which is pretty straight-forward. But unfortunately, I still can not access my Native functionality from JS layer.
Here's how my code looks like.
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;
}
}
And finally, Here's my 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();
}
}
}
}
And here's a JS file where I use my Native module:
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,
});
After adding everything, I clean Android project via ./gradlew clean
and run npm run android
Few notes:
- I have put a log into my Package class just to see if it even gets executed. Unfortunately, log never appeared so I guess it doesn't get executed
- When I run
npm run android
, compilation is successful, but.apk
is failing to launch on the device automatically. Can that be a signal that I have serious issues in my setup?
Here's a log at the end of npm run android
execution:
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
- When I run Android app on the device through
Expo
, I have such logs when the app gets launched:
` Error: Unable to resolve module ./debugger-ui/debuggerWorker.aca173c4 from /Users/superyevhen/Documents/Work/Resources/releases/MakeupReactNativeSampleApp/.:
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/.:`
This message repeats multiple times (7 times actually), but the app is working. I still can modify JS layer successfully and see changes, but my native modules are still not present. Can that be a root cause of an issue?
Any help would be appreciated. I have tried to be as precise as possible but if you need any additional info in order to share an expertise, please let me know. Thanks in advance!
There seems a problem with your
MainApplication.java
. I tried with your file it was having some undefined params.Try this if it works for you.