随着react-native的0.74版本发布,新架构在新版本越来越完善,新架构抛弃了之前的JSBrige方式来调用。官方说这种方式调用调用性能更高。从 0.68 版本开始,React Native 提供了新架构,它为开发者提供了构建高性能和响应式应用的新功能。
新架构里面包含了下面三部分的内容
- 新的原生模块体系 - Turbo Modules,一个支持与原生代码高效、灵活集成的框架。
- Fabric 渲染器和组件,它提供了更好的功能、跨平台的一致性和渲染性能。
- Codegen,它通过 JavaScript 的静态类型化,生成新架构所需的 C++ 模板。
新旧架构对比
旧架构的问题
旧的架构曾经通过使用一个叫做桥(Bridge)的组件将所有必须从 JS 层传递到本地层的数据序列化来工作。桥可以被想象成一条总线,生产者层为消费者层发送一些数据。消费者可以读取数据,将其反序列化并执行所需的操作。
桥有一些固有的限制:
- 它是异步的:某个层将数据提交给桥,再异步地"等待"另一个层来处理它们,即使有时候这并不是真正必要的。
- 它是单线程的:JS 是单线程的,因此发生在 JS 中的计算也必须在单线程上进行。
- 它带来了额外的开销:每当一个层必须使用另一个层时,它就必须序列化一些数据。另一层则必须对其进行反序列化。这里选择的格式是 JSON,因为它的简单性和人的可读性,但尽管是轻量级的,它也是有开销的。
新架构的改进
新架构放弃了"桥"的概念,转而采用另一种通信机制:JavaScript 接口(JSI)。JSI 是一个接口,允许 JavaScript 对象持有对 C++ 的引用,反之亦然。
一旦一个对象拥有另一个对象的引用,它就可以直接调用该对象的方法。例如一个 C++ 对象现在可以直接调用一个 JavaScript 对象在 JavaScript 环境中执行一个方法,反之亦然。
这个想法可以带来几个好处:
- 同步执行:现在可以同步执行那些本来就不应该是异步的函数。
- 并发:可以在 JavaScript 中调用在不同线程上执行的函数。
- 更低的开销:新架构不需要再对数据进行序列化/反序列化,因此可以避免序列化的开销。
- 代码共享:通过引入 C++,现在有可能抽象出所有与平台无关的代码,并在平台之间轻松共享它。
- 类型安全:为了确保 JS 可以正确调用 C++ 对象的方法,反之亦然,因此增加了一层自动生成的代码。这些代码必须通过 Flow 或 TypeScript 类型化的 JS 规范来生成。
CODEING
-
创建一个最新版的
react-native版本项目npx react-native init RN74NewArch然后在项目
RN74NewArch平级创建一个RTNCalculatorTurboModule模块。然后在RTNCalculator下面创建android、ios、js文件夹。Android文件夹存放原生相关的配置和代码,js文件夹下面存放react-native中调用相关的代码。整个项目结构代码如下。 -
在
RTNCalculator的根目录下面android文件夹创建build.gradle文件。buildscript { ext.safeExtGet = {prop, fallback -> rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback } repositories { // 配置为国内的阿里云镜像 maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } mavenCentral() google() gradlePluginPortal() } dependencies { classpath("com.android.tools.build:gradle:7.1.1") } } apply plugin: 'com.android.library' apply plugin: 'com.facebook.react' android { compileSdkVersion safeExtGet('compileSdkVersion', 31) } repositories { maven { url "$projectDir/../node_modules/react-native/android" } mavenCentral() google() } dependencies { implementation 'com.facebook.react:react-native:+' // 此处是你需要调用的原生API 接口三方包。如果是自己撸纯原生, /// 下面可以不用写。这儿模拟封装一个 Picker Image接口给js层调用 implementation 'com.github.gzu-liyujiang.AndroidPicker:Common:4.1.13' implementation 'com.github.gzu-liyujiang.AndroidPicker:WheelView:4.1.13' implementation 'com.github.gzu-liyujiang.AndroidPicker:ImagePicker:4.1.13' } -
在
RTNCalculator/android/src/main下创建AndroidManifest.xml文件。在这里面存放你可能用到的一些配置。<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.rtncalculator"> </manifest> -
在
RTNCalculator/android/src/main/javavcom/rtncalculator中创建CalculatorPackage.java文件package com.rtncalculator; import androidx.annotation.Nullable; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.module.model.ReactModuleInfo; import com.facebook.react.module.model.ReactModuleInfoProvider; import com.facebook.react.TurboReactPackage; import java.util.Collections; import java.util.List; import java.util.HashMap; import java.util.Map; public class CalculatorPackage extends TurboReactPackage { @Nullable @Override public NativeModule getModule(String name, ReactApplicationContext reactContext) { if (name.equals(CalculatorModule.NAME)) { return new CalculatorModule(reactContext); } else { return null; } } @Override public ReactModuleInfoProvider getReactModuleInfoProvider() { return () -> { final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>(); moduleInfos.put( CalculatorModule.NAME, new ReactModuleInfo( CalculatorModule.NAME, CalculatorModule.NAME, false, // canOverrideExistingModule false, // needsEagerInit true, // hasConstants false, // isCxxModule true // isTurboModule )); return moduleInfos; }; } } -
在
RTNCalculator/android/src/main/javavcom/rtncalculator中创建CalculatorModule.java文件package com.rtncalculator; import android.app.Activity; import androidx.annotation.NonNull; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; public class CalculatorModule extends NativeCalculatorSpec { public static String NAME = "RTNCalculator"; CalculatorModule(ReactApplicationContext context) { super(context); } @Override @NonNull public String getName() { return NAME; } @Override public void onCamera(final Promise promise) { final Activity activity = this.getReactApplicationContext().getCurrentActivity(); PickerModuleImpl pickerModule = new PickerModuleImpl(this.getReactApplicationContext()); pickerModule.onCamera(activity, promise); } @Override public void onGallery(Promise promise) { } }-
在
RTNCalculator/android/src/main/javavcom/rtncalculator中创建PickerModuleImpl.java文件package com.rtncalculator; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.util.Log; import android.widget.Toast; import androidx.annotation.Nullable; import com.facebook.react.bridge.ActivityEventListener; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.github.gzuliyujiang.imagepicker.ActivityBuilder; import com.github.gzuliyujiang.imagepicker.CropImageView; import com.github.gzuliyujiang.imagepicker.ImagePicker; import com.github.gzuliyujiang.imagepicker.PickCallback; public class PickerModuleImpl implements ActivityEventListener { public PickerModuleImpl(ReactApplicationContext reactContext) { reactContext.addActivityEventListener(this); } public void onCamera(final Activity activity, final Promise promise) { Log.d("onCamera...", "onCamera: 调用方法"); PickCallback callback = new PickCallback() { @Override public void onPermissionDenied(String[] permissions, String message) { Toast.makeText(activity, message, Toast.LENGTH_SHORT).show(); } @Override public void cropConfig(ActivityBuilder builder) { Log.d("onCamera...", "onCamera: 读取配置"); builder.setMultiTouchEnabled(true) .setGuidelines(CropImageView.Guidelines.ON_TOUCH) .setCropShape(CropImageView.CropShape.OVAL) .setRequestedSize(400, 400) .setFixAspectRatio(true) .setAspectRatio(1, 1); } @Override public void onPickImage(@Nullable Uri imageUri) { String path = String.valueOf(imageUri); Log.d("onCamera...", "onCamera: 获取结果"); Toast.makeText(activity,path, Toast.LENGTH_SHORT).show(); promise.resolve(path); } }; ImagePicker.getInstance().startCamera(activity, true, callback); } @Override public void onActivityResult(Activity activity, int i, int i1, @Nullable Intent data) { Log.d("onCamera", "onActivityResult: "); ImagePicker.getInstance().onActivityResult(activity, i, i1, data); } @Override public void onNewIntent(Intent intent) { } }
-
-
在
RTNCalculator/js中创建NativeCalculator.ts文件,定义在js类型声明import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; import {TurboModuleRegistry} from 'react-native'; export interface Spec extends TurboModule { add(a: number, b: number): Promise<number>; sub(a: number, b: number): Promise<number>; onCamera(): Promise<string>; onGallery(): Promise<string>; } export default TurboModuleRegistry.get<Spec>( 'RTNCalculator', ) as Spec | null;
使用TurBoModule
-
在
RN74NewArch根目录下执行下面命令。安装RTNCalculatorTurboModule。yarn add ../RTNCalculator -
切换到
RN74NewArch/android目录下安装执行下面命令,通过codegen生成原生代码./gradlew generateCodegenArtifactsFromSchema -
导入使用
import React, { useState } from 'react'; import { Button, Image } from 'react-native'; import RTNCalculator from 'rtn-calculator/js/NativeCalculator'; function App(): React.JSX.Element { const [i , setI] = useState() const handlePress = async () =>{ const ret = await RTNCalculator?.onCamera() console.log("......",ret) setI(ret) } return ( <> <Image source={{ uri: i }} style={{ width: 100, height: 100}}/> <Button title='点我选择图片' onPress={handlePress}></Button> </> ); } export default App; -
运行项目
npm run android -
运行效果图