In this guide, I will be going through how to integrate an existing Android application with React Native. This might be useful if you already have an existing Android application and would like to use React Native onwards, whilst still keeping some Android modules that you have already created.
在本指南中,我将介绍如何将现有的Android应用程序与React Native集成。 如果您已经有一个现有的Android应用程序,并且想继续使用React Native,同时仍保留一些已经创建的Android模块,这可能会很有用。
React Native has the documentation on how to integrate it but it did not really fit my use case. React Native docs.
React Native拥有有关如何集成它的文档,但它确实不适合我的用例。 React本地文档 。
There might be issues with the auto-linking feature if you ever face them.
如果您遇到自动链接功能,可能会遇到问题。
The code for this project can be found https://github.com/Danial968/React-Native-Android-Integration.
可以在https://github.com/Danial968/React-Native-Android-Integration中找到该项目的代码。
The use case that I will be tackling is when someone wants to start up a React Native app and go to their older Android modules.
我要解决的用例是有人要启动React Native应用并转到其较旧的Android模块时。
本机应用程序 (The Native App)
I currently have a simple android application that has 2 activities and a single button which leads to the second activity. I will be integrating this application with React Native.
我目前有一个简单的android应用程序,它具有2个活动和一个导致第二个活动的按钮。 我将把这个应用程序与React Native集成在一起。
Package.json (Package.json)
Firstly, create a folder named “android” and place your existing native application within that folder.
首先,创建一个名为“ android”的文件夹,并将您现有的本机应用程序放置在该文件夹中。
Now create a ‘package.json’ within the root folder (not inside the android folder you just created). Edit the file accordingly to your use case.
现在在根文件夹中创建一个“ package.json”(而不是在刚创建的android文件夹中)。 根据您的用例编辑文件。
{ "name": "React Native", "version": "0.0.1", "private": true, "scripts": { "start": "npx react-native start", "android": "npx react-native run-android" }}
Once you have created the ‘package.json’ file open up a terminal and run the following commands. These will install the react-native dependencies and the other dependencies we will need to run the application.
创建“ package.json”文件后,打开一个终端并运行以下命令。 这些将安装本机依赖关系以及运行应用程序所需的其他依赖关系。
npm add react-native
npm add react
npm add hermesvm
npm add jsc-android
build.gradle (build.gradle)
Now open android studio and go into your application’s ‘build.gradle’. Locate the dependencies section.
现在打开android studio并进入应用程序的“ build.gradle”。 找到依赖项部分。
Above the dependencies section, add the following lines of code.
在“依赖性”部分上方,添加以下代码行。
project.ext.react = [
entryFile: "index.js" ,
enableHermes: false,
]
def jscFlavor = 'org.webkit:android-jsc:+'
def enableHermes = project.ext.react.get("enableHermes", false);
Within the dependencies section, add the following lines of code.
在“依赖性”部分中,添加以下代码行。
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation "com.facebook.react:react-native:+" // React Native
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
}
else { implementation jscFlavor }
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'}debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'}debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'}
Lastly at the bottom of the same ‘build.gradle’ file add this line of code
最后,在同一“ build.gradle”文件的底部添加此行代码
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
Now go to the ‘build.gradle’ file within the project folder. It usually has (Project) beside the gradle file in the gradle scripts.
现在转到项目文件夹中的“ build.gradle”文件。 它通常在gradle脚本中的gradle文件旁边有(Project)。
Locate the “allprojects” section and add the following lines of code. Make sure the code is above all other maven repositories.
找到“ allprojects”部分,并添加以下代码行。 确保代码位于所有其他Maven存储库之上。
maven {
// All of React Native (JS, Android binaries) is installed from npm
url ("$rootDir/../node_modules/react-native/android")}maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")}
Define your FLIPPER_VERSION in gradle.properties
在gradle.properties中定义您的FLIPPER_VERSION
FLIPPER_VERSION=0.33.1
Go to settings.gradle and add the following line of code to allow auto-linking
转到settings.gradle并添加以下代码行以允许自动链接
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
Sync your gradle changes.
同步您的gradle更改。
AndroidManifest.xml (AndroidManifest.xml)
Go to AndroidManifest.xml to allow internet permission.
转到AndroidManifest.xml以允许互联网权限。
To add DevSettingsActivity.
添加DevSettingsActivity。
Apply Cleartext Traffic. Starting from API level 28 it is disabled by default and this will prevent you from connecting to your Metro bundler.
应用明文流量。 从API级别28开始,默认情况下它是禁用的,这将阻止您连接到Metro捆绑器。
android:usesCleartextTraffic="${clearText}" tools:targetApi="28"
主要应用 (MainApplication)
Now create a ‘MainApplication.java’ file within your android application. Paste the code below.
现在,在您的android应用程序中创建一个'MainApplication.java'文件。 粘贴下面的代码。
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 getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List packages = new PackageList(this).getPackages();
// 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());
}
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("sg.gov.tech.onemobileapp.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();
}
}
}
}
主要活动 (MainActivity)
Now you will have to create a ‘MainActivity.java’. If you already have one I would suggest renaming it.
现在,您将必须创建一个“ MainActivity.java”。 如果您已经有一个,我建议重命名。
Once you have created the ‘MainActivity.java’, paste the code below. This extends the ReactActivity and it searches for the React Native component to start upon when this class is called in the ‘AndroidManifest.xml’.
创建完“ MainActivity.java”后,粘贴以下代码。 这扩展了ReactActivity,并搜索在“ AndroidManifest.xml”中调用此类时开始的React Native组件。
The return statement in get ‘MainComponentName()’ is the name of the React Native component that you will export in AppRegistry in your ‘index.js’ file that we will create later.
get'MainComponentName()'中的return语句是React Native组件的名称,您将在AppRegistry中将其导出到稍后将创建的'index.js'文件中。
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() { return "MainScreen"; } // Name of the RN component that was exported by AppRegistry.registercomponent()
}
In my case I want the React Native code to be at the start of the launch so I made sure the Activity for MainActivity is still the Launcher and Main.
在我的情况下,我希望React Native代码在启动的开始,因此我确保MainActivity的Activity仍然是Launcher和Main。
Within the application section, add the following snippet.
在“应用程序”部分中,添加以下代码段。
android:name=".MainApplication"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" // This removes the header on the Android app
ReactNativeFlipper (ReactNativeFlipper)
Create a folder called debug with the path android/app/src/java. Within the debug folder create an ‘AndroidManifest.xml’ with the following code.
创建一个名为debug的文件夹,路径为android / app / src / java。 在debug文件夹中,使用以下代码创建“ AndroidManifest.xml”。
Create a java folder within the same folder of the AndroidManifest.xml and follow your package name.
在AndroidManifest.xml的同一文件夹中创建一个Java文件夹,然后按照您的软件包名称进行操作。
Create a ReactNativeFlipper.java file within the folder.
在文件夹中创建一个ReactNativeFlipper.java文件。
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@Override
public void apply(OkHttpClient.Builder builder) {
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}
Index.js (Index.js)
Now we will create a simple React Native file named ‘index.js’.
现在我们将创建一个简单的名为“ index.js”的React Native文件。
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
NativeModules
} from 'react-native';
class MainScreen extends React.Component {
render() {
console.log('The React Native app is running')
return (
Hello, this is a React Native page
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10
}
});
AppRegistry.registerComponent(
'MainScreen', // Name of the component for the Android side to pick up
() => MainScreen
);
Note that the AppRegistry.registerComponent registers the name MainScreen which is the same as the name in ‘MainActivity.java’.
请注意,AppRegistry.registerComponent注册了名称MainScreen,该名称与“ MainActivity.java”中的名称相同。
运行应用程序 (Running the application)
Run the application by going into the file directory and use the command npm run android. If this has some issues you can build the app in android studio and run npm run start to start the metro bundler separately.
通过进入文件目录运行应用程序,并使用命令npm run android。 如果出现问题,您可以在android studio中构建应用,然后运行npm run start分别启动Metro bundler。
Now we are able to start our app with the React Native page as the entry point. However, we are still unable to call the native modules within our React Native code.
现在,我们可以使用React Native页面作为入口启动我们的应用程序。 但是,我们仍然无法在React Native代码中调用本地模块。
导航模块 (NavigationModule)
Create a new class in your android folder called NavigationModule this class will extend ReactContextBaseJavaModule.
在您的android文件夹中创建一个名为NavigationModule的新类,该类将扩展ReactContextBaseJavaModule。
import android.content.Intent;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class NavigationModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;
private Intent intent;
NavigationModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
@NonNull
@Override
public String getName() {
return "NavigationModule";
} //The name of the component when it is called in the RN code
@ReactMethod
public void navigateToNative(){
ReactApplicationContext context = getReactApplicationContext();
intent = new Intent(context,App.class);
if (intent.resolveActivity(context.getPackageManager()) != null) {
intent.setFlags((Intent.FLAG_ACTIVITY_NEW_TASK));
context.startActivity(intent);
}
}
}
In the class, there are 2 methods. The first method getName() will refer to the name of the class when it is called in the React Native code. In this case it will be called NavigationModule.
在课堂上,有2种方法。 在React Native代码中调用第一个方法getName()时将引用该类的名称。 在这种情况下,它将称为NavigationModule。
The second method NavigateToNative() is a method I created and is a ReactMethod. For this case when the method is executed I will want to go to my App class which is my native android activity.
第二个方法NavigateToNative()是我创建的方法,并且是ReactMethod。 对于这种情况,执行该方法时,我将要转到我的App类,这是我的本机android活动。
导航包 (Navigation Package)
Now create a new class called NavigationPackage.java which implements ReactPackage.
现在创建一个名为NavigationPackage.java的新类,该类实现ReactPackage。
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 NavigationPackage implements ReactPackage {
private ReactApplicationContext reactContext;
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List createNativeModules(ReactApplicationContext reactContext) {
List modules = new ArrayList<>();
modules.add(new NavigationModule(reactContext)); // Add the module that you would like to call to from RN
return modules;
}
}
Add the modules that you would like to call from React Native within the createNativeModules method. For this case I am going to add the NavigationModule.
在createNativeModules方法内添加要从React Native调用的模块。 对于这种情况,我将添加NavigationModule。
This package will then have to be added into the getPackages() function in ‘MainApplication.java’. The function is the bridge to the React Native code
然后必须将此包添加到“ MainApplication.java”中的getPackages()函数中。 该功能是React Native代码的桥梁
React本机 (React Native)
To call the methods I will first have to import NativeModules from react-native.
要调用这些方法,我首先必须从react-native导入NativeModules。
import { NativeModules } from 'react-native'
const Navigation = NavativeModules.Navigation
I can call the function that I created in ‘NavigationModule.java’ by putting it as a function from an onPress event.
通过将其作为onPress事件中的函数,可以调用在“ NavigationModule.java”中创建的函数。
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
NativeModules
} from 'react-native';
const Navigation = NativeModules.NavigationModuleclass MainScreen extends React.Component {
render() {
console.log('The React Native app is running')
return (
Hello, this is a React Native page
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10
}
});
AppRegistry.registerComponent(
'MainScreen', // Name of the component for the Android side to pick up
() => MainScreen
);
运行应用 (Run the App)
Now the application should be able to call a native page from React Native.
现在,应用程序应该能够从React Native调用本机页面。
While this process took me several days of trial and error. I hope this article suffices in helping your project. If you are stuck you might want to create an empty React Native application and compare it with yours to see what you are missing from a basic React Native app.
这个过程花了我几天的反复试验。 我希望本文足以帮助您的项目。 如果您陷入困境,则可能要创建一个空的React Native应用程序,并将其与您的应用程序进行比较,以查看基本React Native应用程序中缺少的内容。
翻译自: https://levelup.gitconnected.com/how-to-integrate-an-existing-android-app-with-react-native-6403ac4724f3