Skip to content

Commit eafb345

Browse files
authored
Add one listener per sensor config (software-mansion#4340)
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please follow the template so that the reviewers can easily understand what the code changes affect. --> ## Summary This PR is to optimise using sensors. Before: Each `useAnimatedSensor` call creates a new native listener that update shared value assigned to it. Now: We identify sensors listeners by sensor type and config properties and the same configurations use the same shared value. ## Test plan - Example app ``` import Animated, { useAnimatedStyle, useAnimatedSensor, SensorType, } from 'react-native-reanimated'; import { StyleSheet, View } from 'react-native'; import React from 'react'; export default function AnimatedSensorExample() { const sensorObject = (color: string, sensorType: any) => { const gravity = useAnimatedSensor(sensorType, { interval: 16 }); console.log(gravity.sensor.value); const animatedStyle = useAnimatedStyle(() => { //console.log(gravity.sensor.value); return { top: -gravity.sensor.value.y * 300, left: gravity.sensor.value.x * 200, }; }); return ( <View style={styles.container}> <Animated.View style={[{...styles.box, backgroundColor: color}, animatedStyle]} /> </View> ) }; return ( <View style={styles.container}> {sensorObject('red', SensorType.GRAVITY)} {sensorObject('blue', SensorType.ACCELEROMETER)} {sensorObject('green', SensorType.GRAVITY)} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, box: { width: 150, height: 150, backgroundColor: 'navy', }, }); ```
1 parent f9ef042 commit eafb345

7 files changed

Lines changed: 232 additions & 77 deletions

File tree

__tests__/sensors.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { renderHook, act } from '@testing-library/react-hooks';
2-
import { SensorType, useAnimatedSensor, Value3D, ValueRotation } from '../src/';
2+
import {
3+
SensorConfig,
4+
SensorType,
5+
useAnimatedSensor,
6+
Value3D,
7+
ValueRotation,
8+
} from '../src/';
39

410
let eventHandler: (data: Value3D | ValueRotation) => void;
511

@@ -11,8 +17,7 @@ jest.mock('../src/reanimated2/core', () => {
1117
...originalModule,
1218
registerSensor: (
1319
sensorType: number,
14-
interval: number,
15-
iosReferenceFrame: number,
20+
config: SensorConfig,
1621
_eventHandler: (data: Value3D | ValueRotation) => void
1722
) => {
1823
eventHandler = _eventHandler;

src/reanimated2/Sensor.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import NativeReanimatedModule from './NativeReanimated';
2+
import {
3+
SensorType,
4+
SensorConfig,
5+
SharedValue,
6+
Value3D,
7+
ValueRotation,
8+
ShareableRef,
9+
} from './commonTypes';
10+
import { makeMutable } from './mutables';
11+
12+
function initSensorData(
13+
sensorType: SensorType
14+
): SharedValue<Value3D | ValueRotation> {
15+
if (sensorType === SensorType.ROTATION) {
16+
return makeMutable<Value3D | ValueRotation>({
17+
qw: 0,
18+
qx: 0,
19+
qy: 0,
20+
qz: 0,
21+
yaw: 0,
22+
pitch: 0,
23+
roll: 0,
24+
interfaceOrientation: 0,
25+
});
26+
} else {
27+
return makeMutable<Value3D | ValueRotation>({
28+
x: 0,
29+
y: 0,
30+
z: 0,
31+
interfaceOrientation: 0,
32+
});
33+
}
34+
}
35+
36+
export default class Sensor<T> {
37+
public listenersNumber = 0;
38+
private sensorId: number | null = null;
39+
private sensorType: SensorType;
40+
private data: SharedValue<Value3D | ValueRotation>;
41+
private config: SensorConfig;
42+
43+
constructor(sensorType: SensorType, config: SensorConfig) {
44+
this.sensorType = sensorType;
45+
this.config = config;
46+
this.data = initSensorData(sensorType);
47+
}
48+
49+
register(
50+
eventHandler: ShareableRef<T> | ((data: Value3D | ValueRotation) => void)
51+
) {
52+
const config = this.config;
53+
const sensorType = this.sensorType;
54+
this.sensorId = NativeReanimatedModule.registerSensor(
55+
sensorType,
56+
config.interval === 'auto' ? -1 : config.interval,
57+
config.iosReferenceFrame,
58+
eventHandler
59+
);
60+
return this.sensorId !== -1;
61+
}
62+
63+
isRunning() {
64+
return this.sensorId !== -1 && this.sensorId !== null;
65+
}
66+
67+
isAvailable() {
68+
return this.sensorId !== -1;
69+
}
70+
71+
getSharedValue() {
72+
return this.data;
73+
}
74+
75+
unregister() {
76+
if (this.sensorId !== null && this.sensorId !== -1) {
77+
NativeReanimatedModule.unregisterSensor(this.sensorId);
78+
}
79+
this.sensorId = null;
80+
}
81+
}

src/reanimated2/SensorContainer.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {
2+
SensorType,
3+
SensorConfig,
4+
Value3D,
5+
ValueRotation,
6+
ShareableRef,
7+
SharedValue,
8+
} from './commonTypes';
9+
import Sensor from './Sensor';
10+
11+
export class SensorContainer {
12+
private nativeSensors: Map<number, Sensor<any>> = new Map();
13+
14+
getSensorId(sensorType: SensorType, config: SensorConfig) {
15+
return (
16+
sensorType * 100 +
17+
config.iosReferenceFrame * 10 +
18+
Number(config.adjustToInterfaceOrientation)
19+
);
20+
}
21+
22+
initializeSensor(
23+
sensorType: SensorType,
24+
config: SensorConfig
25+
): SharedValue<Value3D | ValueRotation> {
26+
const sensorId = this.getSensorId(sensorType, config);
27+
28+
if (!this.nativeSensors.has(sensorId)) {
29+
const newSensor = new Sensor(sensorType, config);
30+
this.nativeSensors.set(sensorId, newSensor);
31+
}
32+
33+
const sensor = this.nativeSensors.get(sensorId);
34+
return sensor!.getSharedValue();
35+
}
36+
37+
registerSensor<T>(
38+
sensorType: SensorType,
39+
config: SensorConfig,
40+
handler: ShareableRef<T> | ((data: Value3D | ValueRotation) => void)
41+
): number {
42+
const sensorId = this.getSensorId(sensorType, config);
43+
44+
if (!this.nativeSensors.has(sensorId)) {
45+
return -1;
46+
}
47+
48+
const sensor = this.nativeSensors.get(sensorId);
49+
if (
50+
sensor &&
51+
sensor.isAvailable() &&
52+
(sensor.isRunning() || sensor.register(handler))
53+
) {
54+
sensor.listenersNumber++;
55+
return sensorId;
56+
}
57+
return -1;
58+
}
59+
60+
unregisterSensor(sensorId: number) {
61+
if (this.nativeSensors.has(sensorId)) {
62+
const sensor = this.nativeSensors.get(sensorId);
63+
if (sensor && sensor.isRunning()) {
64+
sensor.listenersNumber--;
65+
if (sensor.listenersNumber === 0) {
66+
sensor.unregister();
67+
}
68+
}
69+
}
70+
}
71+
}

src/reanimated2/commonTypes.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,19 @@ export enum IOSReferenceFrame {
159159
Auto,
160160
}
161161

162+
export type SensorConfig = {
163+
interval: number | 'auto';
164+
adjustToInterfaceOrientation: boolean;
165+
iosReferenceFrame: IOSReferenceFrame;
166+
};
167+
168+
export type AnimatedSensor = {
169+
sensor: SharedValue<Value3D | ValueRotation>;
170+
unregister: () => void;
171+
isAvailable: boolean;
172+
config: SensorConfig;
173+
};
174+
162175
export interface NumericAnimation {
163176
current?: number;
164177
}

src/reanimated2/core.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { nativeShouldBeMock, shouldBeUseWeb, isWeb } from './PlatformChecker';
33
import {
44
AnimatedKeyboardOptions,
55
BasicWorkletFunction,
6+
SensorConfig,
7+
SensorType,
8+
SharedValue,
69
Value3D,
710
ValueRotation,
811
} from './commonTypes';
@@ -20,6 +23,7 @@ import {
2023
LayoutAnimationType,
2124
} from './layoutReanimation';
2225
import { initializeUIRuntime } from './initializers';
26+
import { SensorContainer } from './SensorContainer';
2327

2428
export { stopMapper } from './mappers';
2529
export { runOnJS, runOnUI } from './threads';
@@ -113,6 +117,13 @@ export function getViewProp<T>(viewTag: string, propName: string): Promise<T> {
113117
});
114118
}
115119

120+
export function getSensorContainer(): SensorContainer {
121+
if (!global.__sensorContainer) {
122+
global.__sensorContainer = new SensorContainer();
123+
}
124+
return global.__sensorContainer;
125+
}
126+
116127
export function registerEventHandler<T>(
117128
eventHash: string,
118129
eventHandler: (event: T) => void
@@ -159,24 +170,32 @@ export function unsubscribeFromKeyboardEvents(listenerId: number): void {
159170
}
160171

161172
export function registerSensor(
162-
sensorType: number,
163-
interval: number,
164-
iosReferenceFrame: number,
173+
sensorType: SensorType,
174+
config: SensorConfig,
165175
eventHandler: (
166176
data: Value3D | ValueRotation,
167177
orientationDegrees: number
168178
) => void
169179
): number {
170-
return NativeReanimatedModule.registerSensor(
180+
const sensorContainer = getSensorContainer();
181+
return sensorContainer.registerSensor(
171182
sensorType,
172-
interval,
173-
iosReferenceFrame,
183+
config,
174184
makeShareableCloneRecursive(eventHandler)
175185
);
176186
}
177187

178-
export function unregisterSensor(listenerId: number): void {
179-
return NativeReanimatedModule.unregisterSensor(listenerId);
188+
export function initializeSensor(
189+
sensorType: SensorType,
190+
config: SensorConfig
191+
): SharedValue<Value3D | ValueRotation> {
192+
const sensorContainer = getSensorContainer();
193+
return sensorContainer.initializeSensor(sensorType, config);
194+
}
195+
196+
export function unregisterSensor(sensorId: number): void {
197+
const sensorContainer = getSensorContainer();
198+
return sensorContainer.unregisterSensor(sensorId);
180199
}
181200

182201
// initialize UI runtime if applicable

src/reanimated2/globals.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
} from './commonTypes';
1111
import type { FrameCallbackRegistryUI } from './frameCallback/FrameCallbackRegistryUI';
1212
import type { NativeReanimated } from './NativeReanimated/NativeReanimated';
13+
import type { SensorContainer } from './SensorContainer';
1314
import type {
1415
LayoutAnimationFunction,
1516
LayoutAnimationType,
@@ -90,6 +91,7 @@ declare global {
9091
var __handleCache: WeakMap<object, any>;
9192
var __callMicrotasks: () => void;
9293
var __mapperRegistry: MapperRegistry;
94+
var __sensorContainer: SensorContainer;
9395
var _maybeFlushUIUpdatesQueue: () => void;
9496
var LayoutAnimationsManager: {
9597
start(

0 commit comments

Comments
 (0)