前言
作为一个移动端开发者,移动端的发展及其迅速,无论你是主动还是被动你都得与时俱进。学习新的移动开发技术是不得不做的事情。当前移动开发主流的东西有React Native和flutter,这两个都是以后的大势所趋,所以闲暇之余对React Native进行一个大致的学习。
React Native的中文官网是真的坑,如果完全按照官网来配置的话,只能是一脸懵逼、两脸懵逼、多脸懵逼,搞了半天结果还是不能用。官网文档虽然存在一些问题,但大部分是正确的配置,经过查看试验官方文档和网上的资源,最后终于实现了在Android原生项目中引入React Native,并打包APK成功,为了以后怕忘记如何操作的特此写下配置文档。
准备
本文针对的是Android原生项目接入RN,前期需要一些准备:
-
在Android Studio中新建一个原生的Android项目,例如:ReactNativeApp
-
在项目的根目录下新建一个package.json文件,文件内容如下:
{ "name": "ReactNativeApp", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { "react": "^16.6.3", "react-native": "^0.58.6" } } ---------------------------------------------------------------------------------------------------- 内容介绍: - name “name”是一个很重要的属性,可以理解为接入的这个RN的一个标识,在后面会有作用。这个你自己随便写,但需要记住,。 - version 一个版本信息,没啥作用,随便写一个就好,一般是1.0.0或者0.0.1 - private 是否私有,一般是 true - scripts 一个启动并配置react-native相关信息的命令,这个可以照着写不用修改 - dependencies 顾名思义-依赖,和Android原生中的依赖差不多,这里是添加react和react-native两个依赖,后面的数字是版本号
-
在项目的根目录打开命令窗口(shift+右键)输入以下指令:
npm install --save react react-native
等待几分钟加载完成后,这时看输出内容里面会有一个warn,提示你缺少个“react@版本号”,命令台再输入:
npm add react@版本号
这时整个项目的结构如下:其中node_modules就是刚才加载的文件,comp_demo是我用来存放RN自定义组件的地方,最后将node_modules添加到.gitignore中。
接入
配置Android,使Android可以使用RN相关的东西
-
在app目录下的build.gradle添加以下代码:
android{ ... defaultConfig { ... ndk { abiFilters "armeabi-v7a", "x86" } } } dependencies { implementation 'com.android.support:appcompat-v7:27.1.1' ... //如果想要指定特定的 React Native 版本,可以用具体的版本号替换 +,当然前提是你从 npm 里下载的是这个版本。一般用+号 implementation "com.facebook.react:react-native:+" // From node_modules }
-
在项目目录下的build.gradle添加以下代码(注意):
allprojects { repositories { maven { // 这里不要使用官方的配置,那个有些问题 url "$rootDir/node_modules/react-native/android" } } } 在gradle.properties中添加: android.useDeprecatedNdk=true
-
在AndroidManifest.xml文件中添加权限和开发者菜单界面:
<uses-permission android:name="android.permission.INTERNET" /> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> 将APP的主题theme改为:Theme.AppCompat.Light.NoActionBar
-
在AndroidManifest.xml中添加配置 android:networkSecurityConfig="@xml/network_security_config"
从Android 9(API级别28)开始,默认情况下禁用明文流量;这可以防止您的应用程序连接到React Native打包程序。这些更改添加了域规则,专门允许对打包器IP的明文流量。
在res下创建xml文件夹并创建xml文件,添加以下代码:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <!-- deny cleartext traffic for React Native packager ips in release --> <domain-config cleartextTrafficPermitted="false"> <domain includeSubdomains="false">localhost</domain> <domain includeSubdomains="false">10.0.2.2</domain> <domain includeSubdomains="false">10.0.3.2</domain> </domain-config> </network-security-config>
-
在根目录下新建一个空的index.js文件,添加以下代码(如果有RN项目,可以将其复制进来):
import React from 'react'; import {AppRegistry, StyleSheet, Text, View} from 'react-native'; class demo extends React.Component { render() { return ( <View style={styles.container}> <Text style={styles.hello}>Hello, World</Text> </View> ); } } var styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', }, hello: { fontSize: 20, textAlign: 'center', margin: 10, }, }); // 这里的第一个参数就是第一步中的name,第二个参数是组件名 AppRegistry.registerComponent('ReactNativeApp', () => demo);
-
在Android6.0后需要配置悬浮窗权限
private final int OVERLAY_PERMISSION_REQ_CODE = 1; // 任写一个值 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Toast.makeText(this,"没有权限哎",Toast.LENGTH_LONG).show(); } } } mReactInstanceManager.onActivityResult( this, requestCode, resultCode, data ); }
-
配置BaseApplication,并在AndroidManifest.xml文件中使用
<application
…
android:name=".MainApplication" />
…
public class BaseApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage() ); } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); } }
-
在res/layout中新建activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.facebook.react.ReactRootView android:id="@+id/react_root_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
-
最终的class文件代码如下:
public class MainActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler { private final int OVERLAY_PERMISSION_REQ_CODE = 1; // 任写一个值 private ReactRootView react_root_view ; private ReactInstanceManager mReactInstanceManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initPermission(); initRN(); } @Override protected void onPause() { super.onPause(); if (mReactInstanceManager != null) { mReactInstanceManager.onHostPause(this); } } @Override protected void onResume() { super.onResume(); if (mReactInstanceManager != null) { mReactInstanceManager.onHostResume(this, this); } } @Override protected void onDestroy() { super.onDestroy(); if (mReactInstanceManager != null) { mReactInstanceManager.onHostDestroy(this); } } @Override public void onBackPressed() { if (mReactInstanceManager != null) { mReactInstanceManager.onBackPressed(); } else { super.onBackPressed(); } } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) { mReactInstanceManager.showDevOptionsDialog(); return true; } return super.onKeyUp(keyCode, event); } private void initRN() { mReactInstanceManager = ReactInstanceManager.builder() .setApplication(getApplication()) .setBundleAssetName("index.android.bundle") .setJSMainModulePath("index") .addPackage(new MainReactPackage()) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) .build(); react_root_view.startReactApplication(mReactInstanceManager, "ReactNativeApp", null); } private void initView() { react_root_view = (ReactRootView) findViewById(R.id.react_root_view); } private void initPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); } } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { // SYSTEM_ALERT_WINDOW permission not granted Log.e("信息:","permission not granted"); } } } mReactInstanceManager.onActivityResult( this, requestCode, resultCode, data ); } @Override public void invokeDefaultOnBackPressed() { super.onBackPressed(); } }
-
启动包服务器,在项目根目录下打开命令行输入以下指令:
npm start
-
运行APP
打包
-
在src/main下创建文件夹assets,并在改文件夹下创建文件index.android.bundle
-
在Android Studio的Terminal中输入:
react-native bundle --platform android --dev false --entry-file index.js --bundle-output ./app/src/main/assets/index.android.bundle --assets-dest ./app/src/main/res/
-
配置Key store…之后的打包操作和Android一样