对于一个新的工程,可以直接使用React Native进行开发,但是对于现有的项目如果全面改造将会是一项巨大的工程。好在RN提供了方式为我们接入现有工程。同时,对于Android中的一些组件,RN不一定有已经继承的实现方式,我们可以通过实现ReactMethod的方式实现RN使用Android原生组件。

1. 接入React Native

1. RN开发环境准备及Android原生项目

这里不再赘述,按照搭建开发环境便可以轻松实现对于RN环境的搭建。

原生项目相信大家也一定会建立的~

2. 安装 JavaScript 依赖包

在项目根目录下创建一个名为package.json的空文本文件,并在其中填入以下内容

{  "name": "RNHybridDemo",//可自定义  "version": "0.0.1",//可自定义  "private": true,  "scripts": {    "start": "node node_modules/react-native/local-cli/cli.js start" //必填  }}

其中scripts中是用于启动 packager 服务的命令。

接下来我们使用 yarn 或 npm(两者都是 node 的包管理器)来安装 React 和 React Native 模块(推荐使用yarn)。打开一个终端/命令提示行,进入到项目根目录(即包含有 package.json 文件的目录),然后运行下列命令来安装:

$ yarn add react-native

这样默认会安装最新版本的 React Native,同时会打印出类似下面的警告信息

warning “react-native@0.52.2” has unmet peer dependency “react@16.6.1”.

这是正常的现象,我们还需要安装指定的React

$ yarn add react@16.6.1

注意必须严格匹配警告信息中所列出的版本,高了或者低了都不可以。

最后,所有 JavaScript 依赖模块都会被安装到项目根目录下的node_modules/目录中(这个目录我们原则上不复制、不移动、不修改、不上传,随用随装)。把node_modules/目录记录到.gitignore文件中(即不上传到版本控制系统,只保留在本地)。

3. 把 React Native 添加到你的应用中

首先我们配置gradle。在你的app中build.gradle文件中添加 React Native 依赖:

dependencies {    ...    implementation "com.facebook.react:react-native:+"}

如果想要指定特定的 React Native 版本,可以用具体的版本号替换 +,当然前提是你从 npm 里下载的是这个版本。

接下来是在项目的build.gradle文件中为 React Native 添加一个 maven 依赖的入口,必须写在 allprojects代码块中:

allprojects {    repositories {    ...    maven {            url "$rootDir/node_modules/react-native/android"        }    }}

这里需要主要一定是上述的,在RN教程中是url "$rootDir/../node_modules/react-native/android",但是这样会导致在gradle编译时找不到我们所需要的RN包。这里的坑我呆了半天才爬出来

4. 配置权限

接着,在 AndroidManifest.xml 清单文件中声明网络权限:

<uses-permission android:name="android.permission.INTERNET" />

如果需要访问 DevSettingsActivity 界面(即开发者菜单),则还需要在 AndroidManifest.xml 中声明:

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

开发者菜单一般仅用于在开发时从 Packager 服务器刷新 JavaScript 代码,所以在正式发布时你可以去掉这一权限。

5. 代码继承

首先是在项目根目录中创建一个index.js文件,index.js是 React Native 应用在 Android 上的入口文件。而且它是不可或缺的!它可以是个很简单的文件,简单到可以只包含一行require/import导入语句。

其实是实现RN在原生页面的展现。如果你的应用会运行在 Android 6.0(API level 23)或更高版本,请确保你在开发版本中有打开悬浮窗(overlay)权限。

这里直接放出页面的代码吧(Kotlin),记得在Manifest中注册页面:

class MyReactActivity : Activity(), DefaultHardwareBackBtnHandler {    private lateinit var mReactRootView: ReactRootView    private lateinit var mReactInstanceManager: ReactInstanceManager    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        requestOverlayPermission()        initView()    }    private fun initView() {        mReactRootView = ReactRootView(this)        mReactInstanceManager = ReactInstanceManager.builder()            .setApplication(application)            .setBundleAssetName("index.android.bundle")//设置加载bundle文件在asserts中的路径            .setJSMainModulePath("index")//在开发模式时入口js文件名称,仅在开发模式时起作用            .addPackage(MainReactPackage())//自定义Package            .setUseDeveloperSupport(BuildConfig.DEBUG)//是否是Debug模式            .setInitialLifecycleState(LifecycleState.RESUMED)//初始化React时的生命周期            .build()        // 注意这里的MyReactNativeApp必须对应“index.js”中的        // “AppRegistry.registerComponent()”的第一个参数        mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null)        setContentView(mReactRootView)    }    private fun requestOverlayPermission() {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            if (!Settings.canDrawOverlays(this)) {                val intent = Intent(                    Settings.ACTION_MANAGE_OVERLAY_PERMISSION,                    Uri.parse("package:$packageName")                )                startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE)            }        }    }    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {        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                }            }        }        mReactInstanceManager.onActivityResult(this, requestCode, resultCode, data)    }    override fun invokeDefaultOnBackPressed() {        super.onBackPressed()    }    override fun onPause() {        super.onPause()        mReactInstanceManager.onHostPause(this)    }    override fun onResume() {        super.onResume()        mReactInstanceManager.onHostResume(this, this)    }    override fun onDestroy() {        super.onDestroy()        mReactInstanceManager.onHostDestroy(this)        mReactRootView.unmountReactApplication()    }    override fun onBackPressed() {        mReactInstanceManager.onBackPressed()    }    override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {        if (keyCode == KeyEvent.KEYCODE_MENU) {            mReactInstanceManager.showDevOptionsDialog()            return true        }        return super.onKeyUp(keyCode, event)    }    companion object {        var OVERLAY_PERMISSION_REQ_CODE = 1234    }}

这样对于RN的集成就初步完成了,你可以编写index.js来实现页面。

在初步运行时可能会存在问题:

  1. 在真机调试时由于index.js获取不到导致的错误

解决:在App>src>main中创建assets目录,在调试前运行下面代码,这样在每次编译打包之前需要先执行 js 文件的打包(即生成离线的 jsbundle 文件)。:

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/
  1. 部分真机由于so库对于64位的不兼容导致错误

解决:限制使用32位的so库

  1. gradle.properties中添加android.useDeprecatedNdk=true
  2. app目录下的build.gradle添加下属代码:
android {    ...    defaultConfig {       ...        ndk {            abiFilters "armeabi-v7a", "x86"        }    }}

2. RN与原生组件的通信

有时候 App 需要访问平台 API,但 React Native 可能还没有相应的模块包装;或者你需要复用一些 Java 代码,而不是用 Javascript 重新实现一遍;又或者你需要实现某些高性能的、多线程的代码,譬如图片处理、数据库、或者各种高级扩展等等。

这时就需要实现通信了。

这里以toast为例子来实现一个。

  1. 创建一个ToastModule:

    ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在 JavaScript 端标记这个模块。这里我们把这个模块叫做ToastExample,这样就可以在 JavaScript 中通过NativeModules.ToastExample访问到这个模块。

    要导出一个方法给 JavaScript 使用,Java 方法需要使用注解@ReactMethod。方法的返回类型必须为void。React Native 的跨语言访问是异步进行的,所以想要给 JavaScript 返回一个值的唯一办法是使用回调函数或者发送事件。

    class ToastModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {    override fun getName(): String {        return "ToastExample"    }    override fun getConstants(): Map<String, Any>? {        val constants = mutableMapOf<String, Any>()        constants[DURATION_SHORT_KEY] = Toast.LENGTH_SHORT        constants[DURATION_LONG_KEY] = Toast.LENGTH_LONG        return constants    }    @ReactMethod    fun showToast(message: String, duration: Int) {        Toast.makeText(reactApplicationContext, message, duration).show()    }    companion object {        private const val DURATION_SHORT_KEY = "SHORT"        private const val DURATION_LONG_KEY = "LONG"    }}
  2. 注册模块

    我们需要在应用的 Package 类的createNativeModules方法中添加这个模块。如果模块没有被注册,它也无法在 JavaScript 中被访问到。

    class ReactPackage : ReactPackage {    override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {        val modules = mutableListOf<NativeModule>()        modules.add(ToastModule(reactContext))        return modules    }    override fun createViewManagers(reactContext: ReactApplicationContext?): MutableList<ViewManager<View, ReactShadowNode<*>>> {        return Collections.emptyList()    }}
  3. 在Application中引入包

    class MyApplication : Application(), ReactApplication {    override fun onCreate() {        super.onCreate()        SoLoader.init(this, false)    }    private val mReactNativeHost = object : ReactNativeHost(this) {        override fun getUseDeveloperSupport(): Boolean {            return BuildConfig.DEBUG        }        override fun getPackages(): List<ReactPackage> {            return Arrays.asList(                MainReactPackage(),                ReactPackage()            )        }    }    override fun getReactNativeHost(): ReactNativeHost {        return mReactNativeHost    }}
  4. 实现index.js

    在js文件中添加NativeModules的引用。

    import React, { Component } from 'react';import {  AppRegistry,  StyleSheet,  NativeModules,  Button,  View} from 'react-native';var RNToast = NativeModules.ToastExample;export default class HelloWorld extends Component {  render() {    return (      <View style={styles.container}>        <View style={styles.buttonContainer}>          <Button            onPress={() => {                RNToast.showToast("test",RNToast.LONG);              }}            title="Press Me"          />        </View>      </View>    );  }}const styles = StyleSheet.create({  container: {   flex: 1,   justifyContent: 'center',  },  buttonContainer: {    margin: 20  },  alternativeLayoutButtonContainer: {    margin: 20,    flexDirection: 'row',    justifyContent: 'space-between'  }})AppRegistry.registerComponent('MyReactNativeApp', () => HelloWorld);
  5. Last but not least:在实现的原生页面中(MyReactActivity)也需要添加包

    这一点并没有说明,我也是摸索了很久才想起来的。(当然要是有不足之处请给位指出)

    .addPackage(ReactPackage())

以上便实现了原生通信。

React Native接入现有Android原生工程并实现简单的RN与Android通信_第1张图片

更多相关文章

  1. Android中的gen文件为空或者不存在的处理方法
  2. 第一行代码:AlertDialog
  3. Android 自动更新代码
  4. Android APK 扩展文件
  5. Android 自制一个工作日历 原代码
  6. Android颜色值XML文件
  7. android 查看apk中资源文件
  8. Android 根文件系统启动分析
  9. android 网络视频代码

随机推荐

  1. Android Studio创建计算器Demo
  2. Android apk静默安装与卸载7.0 9.0
  3. 深入浅析Android的自定义布局
  4. android之webView加载javascropt
  5. struts2服务端与android交互
  6. android 透明度
  7. 关于Android中xml布局文件
  8. 基于Platinum库的DMR实现(android)
  9. android进度条的样式
  10. android ListView 中getview学习总结