chore: update map

This commit is contained in:
2026-02-19 09:03:43 +01:00
parent 900c54c20b
commit 823ea7e739
64 changed files with 1839 additions and 3662 deletions
+76 -293
View File
@@ -1,319 +1,102 @@
project(Reanimated)
cmake_minimum_required(VERSION 3.8)
set (CMAKE_VERBOSE_MAKEFILE ON)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 73)
set (CMAKE_CXX_STANDARD 20)
else()
set (CMAKE_CXX_STANDARD 17)
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# default CMAKE_CXX_FLAGS: "-g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-protector-all"
set(CMAKE_CXX_STANDARD 20)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
add_compile_options(${folly_FLAGS})
else()
set(folly_FLAGS "-DFOLLY_NO_CONFIG=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_HAVE_MEMRCHR=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_MOBILE=1 -DFOLLY_HAVE_RECVMMSG=1 -DFOLLY_HAVE_PTHREAD=1")
string(APPEND CMAKE_CXX_FLAGS " ${folly_FLAGS}")
endif()
# default CMAKE_CXX_FLAGS: "-g -DANDROID -fdata-sections -ffunction-sections
# -funwind-tables -fstack-protector-strong -no-canonical-prefixes
# -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-protector-all"
include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
add_compile_options(${folly_FLAGS})
string(APPEND CMAKE_CXX_FLAGS " -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION} -DREANIMATED_VERSION=${REANIMATED_VERSION} -DHERMES_ENABLE_DEBUGGER=${HERMES_ENABLE_DEBUGGER}")
string(
APPEND
CMAKE_CXX_FLAGS
" -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}\
-DREANIMATED_VERSION=${REANIMATED_VERSION}\
-DREANIMATED_FEATURE_FLAGS=\"${REANIMATED_FEATURE_FLAGS}\"")
string(APPEND CMAKE_CXX_FLAGS " -fno-omit-frame-pointer -fstack-protector-all")
# flags to optimize the binary size
string(APPEND CMAKE_CXX_FLAGS " -fvisibility=hidden -ffunction-sections -fdata-sections")
string(APPEND CMAKE_CXX_FLAGS " -fexceptions -fno-omit-frame-pointer -frtti -fstack-protector-all -std=c++${CMAKE_CXX_STANDARD} -Wall -Werror")
if(${IS_NEW_ARCHITECTURE_ENABLED})
string(APPEND CMAKE_CXX_FLAGS " -DRCT_NEW_ARCH_ENABLED")
if(${REANIMATED_PROFILING})
string(APPEND CMAKE_CXX_FLAGS " -DREANIMATED_PROFILING")
endif()
if(${IS_REANIMATED_EXAMPLE_APP})
string(APPEND CMAKE_CXX_FLAGS " -DIS_REANIMATED_EXAMPLE_APP")
string(APPEND CMAKE_CXX_FLAGS " -DIS_REANIMATED_EXAMPLE_APP -Wpedantic")
endif()
if(NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug")
string(APPEND CMAKE_CXX_FLAGS " -DNDEBUG")
string(APPEND CMAKE_CXX_FLAGS " -DNDEBUG")
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
set(BUILD_DIR "${CMAKE_SOURCE_DIR}/build")
set(ANDROID_CPP_DIR "${CMAKE_SOURCE_DIR}/src/main/cpp")
set(COMMON_CPP_DIR "${CMAKE_SOURCE_DIR}/../Common/cpp")
set (PACKAGE_NAME "reanimated")
set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
set (SRC_DIR ${CMAKE_SOURCE_DIR}/src)
set (COMMON_SRC_DIR "${CMAKE_SOURCE_DIR}/../Common")
file(GLOB_RECURSE REANIMATED_COMMON_CPP_SOURCES CONFIGURE_DEPENDS
"${COMMON_CPP_DIR}/reanimated/*.cpp")
file(GLOB_RECURSE REANIMATED_ANDROID_CPP_SOURCES CONFIGURE_DEPENDS
"${ANDROID_CPP_DIR}/reanimated/*.cpp")
file(GLOB_RECURSE SOURCES_COMMON CONFIGURE_DEPENDS "${COMMON_SRC_DIR}/cpp/**.cpp")
file(GLOB_RECURSE SOURCES_ANDROID CONFIGURE_DEPENDS "${SRC_DIR}/main/cpp/**.cpp")
find_package(fbjni REQUIRED CONFIG)
find_package(ReactAndroid REQUIRED CONFIG)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
# Consume shared libraries and headers from prefabs
find_package(fbjni REQUIRED CONFIG)
find_package(ReactAndroid REQUIRED CONFIG)
if(${JS_RUNTIME} STREQUAL "hermes")
find_package(hermes-engine REQUIRED CONFIG)
endif()
add_library(reanimated SHARED ${REANIMATED_COMMON_CPP_SOURCES}
${REANIMATED_ANDROID_CPP_SOURCES})
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 80)
include(
"${REACT_NATIVE_DIR}/ReactCommon/cmake-utils/react-native-flags.cmake")
target_compile_reactnative_options(reanimated PUBLIC)
else()
# Consume shared libraries from found .so files
if(${IS_NEW_ARCHITECTURE_ENABLED})
message(FATAL_ERROR "not supported")
else()
set (RN_SO_DIR "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/first-party/react/jni")
set (FBJNI_HEADERS_DIR "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/first-party/fbjni/headers")
endif()
file (GLOB LIBRN_DIR "${RN_SO_DIR}/${ANDROID_ABI}")
string(APPEND CMAKE_CXX_FLAGS
" -fexceptions -frtti -std=c++${CMAKE_CXX_STANDARD} -Wall -Werror")
endif()
add_library(
${PACKAGE_NAME}
SHARED
${SOURCES_COMMON}
${SOURCES_ANDROID}
${INCLUDE_JSI_CPP}
${INCLUDE_JSIDYNAMIC_CPP}
)
# includes
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${COMMON_SRC_DIR}/cpp/AnimatedSensor"
"${COMMON_SRC_DIR}/cpp/Fabric"
"${COMMON_SRC_DIR}/cpp/hidden_headers"
"${COMMON_SRC_DIR}/cpp/LayoutAnimations"
"${COMMON_SRC_DIR}/cpp/NativeModules"
"${COMMON_SRC_DIR}/cpp/ReanimatedRuntime"
"${COMMON_SRC_DIR}/cpp/Registries"
"${COMMON_SRC_DIR}/cpp/SharedItems"
"${COMMON_SRC_DIR}/cpp/Tools"
"${SRC_DIR}/main/cpp"
reanimated
PRIVATE "${COMMON_CPP_DIR}"
"${ANDROID_CPP_DIR}"
"${REACT_NATIVE_DIR}/ReactCommon"
"${REACT_NATIVE_DIR}/ReactCommon/yoga"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
"${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx"
"${REACT_NATIVE_WORKLETS_DIR}/Common/cpp"
"${REACT_NATIVE_WORKLETS_DIR}/android/src/main/cpp")
set_target_properties(reanimated PROPERTIES LINKER_LANGUAGE CXX)
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(BUILD_TYPE "debug")
else()
set(BUILD_TYPE "release")
endif()
add_library(worklets SHARED IMPORTED)
set_target_properties(
worklets
PROPERTIES
IMPORTED_LOCATION
"${REACT_NATIVE_WORKLETS_DIR}/android/build/intermediates/cmake/${BUILD_TYPE}/obj/${ANDROID_ABI}/libworklets.so"
)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
"${REACT_NATIVE_DIR}/ReactCommon"
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
"${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx"
"${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/yoga"
)
set_target_properties(reanimated PROPERTIES LINKER_LANGUAGE CXX)
# remove dead code sections
set_target_properties(reanimated PROPERTIES LINK_FLAGS "-Wl,--gc-sections")
target_link_libraries(reanimated log ReactAndroid::jsi fbjni::fbjni android
worklets)
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
target_link_libraries(reanimated ReactAndroid::reactnative)
else()
file(GLOB LIBFBJNI_INCLUDE_DIR ${FBJNI_HEADERS_DIR})
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${LIBFBJNI_INCLUDE_DIR}"
"${BUILD_DIR}/third-party-ndk/boost/boost_${BOOST_VERSION}"
"${BUILD_DIR}/third-party-ndk/double-conversion"
"${BUILD_DIR}/third-party-ndk/folly"
"${BUILD_DIR}/third-party-ndk/glog/exported"
"${REACT_NATIVE_DIR}/React"
"${REACT_NATIVE_DIR}/React/Base"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni"
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/java/com/facebook/react/fabric/jni"
"${REACT_NATIVE_DIR}/ReactCommon"
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
"${REACT_NATIVE_DIR}/ReactCommon/jsi"
"${REACT_NATIVE_DIR}/ReactCommon/hermes"
"${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx"
"${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor"
"${REACT_NATIVE_DIR}/ReactCommon/turbomodule/core"
"${REACT_NATIVE_DIR}/ReactCommon/turbomodule"
"${REACT_NATIVE_DIR}/ReactCommon/yoga"
)
target_link_libraries(reanimated ReactAndroid::react_nativemodule_core)
endif()
# build shared lib
set_target_properties(${PACKAGE_NAME} PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(
${PACKAGE_NAME}
log
android
)
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
target_link_libraries(
${PACKAGE_NAME}
ReactAndroid::folly_runtime
ReactAndroid::glog
ReactAndroid::jsi
ReactAndroid::reactnativejni
fbjni::fbjni
)
else()
find_library(
JSI_LIB
jsi
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
find_library(
GLOG_LIB
glog
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
find_library(
FBJNI_LIB
fbjni
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
if(${REACT_NATIVE_MINOR_VERSION} LESS 69)
find_library(
FOLLY_LIB
folly_json
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
else()
find_library(
FOLLY_LIB
folly_runtime
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
endif()
find_library(
REACTNATIVEJNI_LIB
reactnativejni
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
target_link_libraries(
${PACKAGE_NAME}
${JSI_LIB}
${GLOG_LIB}
${FBJNI_LIB}
${FOLLY_LIB}
${REACTNATIVEJNI_LIB}
)
endif()
if(${JS_RUNTIME} STREQUAL "hermes")
string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_HERMES=1")
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
# From prefab from module `com.facebook.react:hermes-android`
set(HERMES_LIB hermes-engine::libhermes)
elseif(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 69)
# Bundled Hermes from module `com.facebook.react:hermes-engine` or project `:ReactAndroid:hermes-engine`
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${JS_RUNTIME_DIR}/API"
"${JS_RUNTIME_DIR}/public"
)
set(HERMES_LIB "${BUILD_DIR}/third-party-ndk/hermes/jni/${ANDROID_ABI}/libhermes.so")
else()
# From `hermes-engine` npm package
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${JS_RUNTIME_DIR}/android/include"
)
set(HERMES_LIB "${BUILD_DIR}/third-party-ndk/hermes/jni/${ANDROID_ABI}/libhermes.so")
endif()
target_link_libraries(
${PACKAGE_NAME}
${HERMES_LIB}
)
if (${HERMES_ENABLE_DEBUGGER})
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
set(HERMES_EXECUTOR_LIB ReactAndroid::hermes_executor)
else()
find_library(
HERMES_EXECUTOR_LIB
hermes-executor-debug
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
endif()
if(${REACT_NATIVE_MINOR_VERSION} LESS_EQUAL 67)
find_library(
HERMES_INSPECTOR_LIB
hermes-inspector
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
target_link_libraries(
${PACKAGE_NAME}
${HERMES_INSPECTOR_LIB}
)
endif()
target_link_libraries(
${PACKAGE_NAME}
${HERMES_EXECUTOR_LIB}
)
endif()
elseif(${JS_RUNTIME} STREQUAL "v8")
string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_V8=1")
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${JS_RUNTIME_DIR}/src"
)
file (GLOB V8_SO_DIR "${JS_RUNTIME_DIR}/android/build/intermediates/library_jni/*/jni/${ANDROID_ABI}")
find_library(
V8EXECUTOR_LIB
v8executor
PATHS ${V8_SO_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
target_link_libraries(
${PACKAGE_NAME}
${V8EXECUTOR_LIB}
)
elseif(${JS_RUNTIME} STREQUAL "jsc")
string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_JSC=1")
if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 71)
set(JSEXECUTOR_LIB ReactAndroid::jscexecutor)
else()
find_library(
JSEXECUTOR_LIB
jscexecutor
PATHS ${LIBRN_DIR}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
endif()
target_link_libraries(${PACKAGE_NAME} ${JSEXECUTOR_LIB})
else()
message(FATAL_ERROR "Unknown JS runtime ${JS_RUNTIME}.")
endif()
if(${IS_NEW_ARCHITECTURE_ENABLED})
target_link_libraries(
${PACKAGE_NAME}
ReactAndroid::fabricjni
ReactAndroid::react_debug
ReactAndroid::react_render_core
ReactAndroid::react_render_mounting
ReactAndroid::react_render_scheduler
ReactAndroid::react_render_uimanager
ReactAndroid::rrc_view
)
endif()
# Resolves "CMake Warning: Manually-specified variables were not used by the project"
# when any of the following variables is not used in some build configuration.
set (ignoreMe "${JS_RUNTIME_DIR}")
set (ignoreMe "${BOOST_VERSION}")
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -1,2 +1,3 @@
-keep class com.swmansion.reanimated.** { *; }
-keep class com.facebook.react.turbomodule.** { *; }
-keep class com.facebook.react.fabric.** { *; }
@@ -3,7 +3,7 @@ apply plugin: 'com.diffplug.spotless'
spotless {
java {
target 'src/main/java/**/*.java'
target 'src/**/*.java'
googleJavaFormat()
}
}
@@ -1,5 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.swmansion.reanimated">
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"/>
@@ -1,103 +1,49 @@
package com.swmansion.reanimated;
import static java.lang.Float.NaN;
import android.view.View;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.GuardedRunnable;
import com.facebook.react.bridge.JavaOnlyMap;
import android.os.SystemClock;
import androidx.tracing.Trace;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.GuardedFrameCallback;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.UIImplementation;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.UIManagerReanimatedHelper;
import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcherListener;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.swmansion.reanimated.layoutReanimation.AnimationsManager;
import com.swmansion.reanimated.nativeProxy.NoopEventHandler;
import com.swmansion.worklets.WorkletsModule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
public class NodesManager implements EventDispatcherListener {
public void scrollTo(int viewTag, double x, double y, boolean animated) {
View view;
try {
view = mUIManager.resolveView(viewTag);
} catch (IllegalViewOperationException e) {
e.printStackTrace();
return;
}
NativeMethodsHelper.scrollTo(view, x, y, animated);
}
public void dispatchCommand(int viewTag, String commandId, ReadableArray commandArgs) {
// mUIManager.dispatchCommand must be called from native modules queue thread
// because of an assert in ShadowNodeRegistry.getNode
mContext.runOnNativeModulesQueueThread(
new GuardedRunnable(mContext.getExceptionHandler()) {
@Override
public void runGuarded() {
mUIManager.dispatchCommand(viewTag, commandId, commandArgs);
}
});
}
public float[] measure(int viewTag) {
View view;
try {
view = mUIManager.resolveView(viewTag);
} catch (IllegalViewOperationException e) {
e.printStackTrace();
return (new float[] {NaN, NaN, NaN, NaN, NaN, NaN});
}
return NativeMethodsHelper.measure(view);
}
private Long mFirstUptime = SystemClock.uptimeMillis();
private boolean mSlowAnimationsEnabled = false;
private int mAnimationsDragFactor;
public interface OnAnimationFrame {
void onAnimationFrame(double timestampMs);
}
private AnimationsManager mAnimationManager;
private final UIImplementation mUIImplementation;
private final DeviceEventManagerModule.RCTDeviceEventEmitter mEventEmitter;
private final ReactChoreographer mReactChoreographer;
private final GuardedFrameCallback mChoreographerCallback;
protected final UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
private final AtomicBoolean mCallbackPosted = new AtomicBoolean();
private final ReactContext mContext;
private final UIManagerModule mUIManager;
private ReactApplicationContext mReactApplicationContext;
private RCTEventEmitter mCustomEventHandler = new NoopEventHandler();
private List<OnAnimationFrame> mFrameCallbacks = new ArrayList<>();
private ConcurrentLinkedQueue<CopiedEvent> mEventQueue = new ConcurrentLinkedQueue<>();
private double lastFrameTimeMs;
public Set<String> uiProps = Collections.emptySet();
public Set<String> nativeProps = Collections.emptySet();
private ReaCompatibility compatibility;
private FabricUIManager mFabricUIManager;
public NativeProxy getNativeProxy() {
return mNativeProxy;
@@ -105,47 +51,23 @@ public class NodesManager implements EventDispatcherListener {
private NativeProxy mNativeProxy;
public AnimationsManager getAnimationsManager() {
return mAnimationManager;
}
public void onCatalystInstanceDestroy() {
if (mAnimationManager != null) {
mAnimationManager.onCatalystInstanceDestroy();
}
public void invalidate() {
if (mNativeProxy != null) {
mNativeProxy.onCatalystInstanceDestroy();
mNativeProxy.invalidate();
mNativeProxy = null;
}
}
public void initWithContext(ReactApplicationContext reactApplicationContext) {
mReactApplicationContext = reactApplicationContext;
mNativeProxy = new NativeProxy(reactApplicationContext);
mAnimationManager.setAndroidUIScheduler(getNativeProxy().getAndroidUIScheduler());
compatibility = new ReaCompatibility(reactApplicationContext);
compatibility.registerFabricEventListener(this);
}
private final class NativeUpdateOperation {
public int mViewTag;
public WritableMap mNativeProps;
public NativeUpdateOperation(int viewTag, WritableMap nativeProps) {
mViewTag = viewTag;
mNativeProps = nativeProps;
if (mFabricUIManager != null) {
mFabricUIManager.getEventDispatcher().removeListener(this);
}
}
private Queue<NativeUpdateOperation> mOperationsInBatch = new LinkedList<>();
private boolean mTryRunBatchUpdatesSynchronously = false;
public NodesManager(ReactApplicationContext context, WorkletsModule workletsModule) {
context.assertOnJSQueueThread();
public NodesManager(ReactContext context) {
mContext = context;
mUIManager = context.getNativeModule(UIManagerModule.class);
mUIImplementation = mUIManager.getUIImplementation();
mCustomEventNamesResolver = mUIManager.getDirectEventNamesResolver();
UIManager uiManager = UIManagerHelper.getUIManager(context, UIManagerType.FABRIC);
assert uiManager != null;
mCustomEventNamesResolver = uiManager::resolveCustomDirectEventName;
mEventEmitter = context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
mReactChoreographer = ReactChoreographer.getInstance();
@@ -157,17 +79,9 @@ public class NodesManager implements EventDispatcherListener {
}
};
// We register as event listener at the end, because we pass `this` and we haven't finished
// contructing an object yet.
// This lead to a crash described in
// https://github.com/software-mansion/react-native-reanimated/issues/604 which was caused by
// Nodes Manager being constructed on UI thread and registering for events.
// Events are handled in the native modules thread in the `onEventDispatch()` method.
// This method indirectly uses `mChoreographerCallback` which was created after event
// registration, creating race condition
mUIManager.getEventDispatcher().addListener(this);
mAnimationManager = new AnimationsManager(mContext, mUIManager);
mNativeProxy = new NativeProxy(context, workletsModule, this);
mFabricUIManager = (FabricUIManager) uiManager;
mFabricUIManager.getEventDispatcher().addListener(this);
}
public void onHostPause() {
@@ -202,99 +116,58 @@ public class NodesManager implements EventDispatcherListener {
}
public void performOperations() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
if (mNativeProxy != null) {
mNativeProxy.performOperations();
}
} else if (!mOperationsInBatch.isEmpty()) {
final Queue<NativeUpdateOperation> copiedOperationsQueue = mOperationsInBatch;
mOperationsInBatch = new LinkedList<>();
final boolean trySynchronously = mTryRunBatchUpdatesSynchronously;
mTryRunBatchUpdatesSynchronously = false;
final Semaphore semaphore = new Semaphore(0);
mContext.runOnNativeModulesQueueThread(
new GuardedRunnable(mContext.getExceptionHandler()) {
@Override
public void runGuarded() {
boolean queueWasEmpty =
UIManagerReanimatedHelper.isOperationQueueEmpty(mUIImplementation);
boolean shouldDispatchUpdates = trySynchronously && queueWasEmpty;
if (!shouldDispatchUpdates) {
semaphore.release();
}
while (!copiedOperationsQueue.isEmpty()) {
NativeUpdateOperation op = copiedOperationsQueue.remove();
ReactShadowNode shadowNode = mUIImplementation.resolveShadowNode(op.mViewTag);
if (shadowNode != null) {
mUIManager.updateView(op.mViewTag, shadowNode.getViewClass(), op.mNativeProps);
}
}
if (queueWasEmpty) {
mUIImplementation.dispatchViewUpdates(-1); // no associated batchId
}
if (shouldDispatchUpdates) {
semaphore.release();
}
}
});
if (trySynchronously) {
try {
semaphore.tryAcquire(16, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// if the thread is interruped we just continue and let the layout update happen
// asynchronously
}
}
UiThreadUtil.assertOnUiThread();
if (mNativeProxy != null) {
mNativeProxy.performOperations();
}
}
private void onAnimationFrame(long frameTimeNanos) {
// Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onAnimationFrame");
UiThreadUtil.assertOnUiThread();
double currentFrameTimeMs = frameTimeNanos / 1000000.;
if (currentFrameTimeMs > lastFrameTimeMs) {
// It is possible for ChoreographerCallback to be executed twice within the same frame
// due to frame drops. If this occurs, the additional callback execution should be ignored.
lastFrameTimeMs = currentFrameTimeMs;
while (!mEventQueue.isEmpty()) {
CopiedEvent copiedEvent = mEventQueue.poll();
handleEvent(
copiedEvent.getTargetTag(), copiedEvent.getEventName(), copiedEvent.getPayload());
try {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.beginSection("onAnimationFrame");
}
if (!mFrameCallbacks.isEmpty()) {
List<OnAnimationFrame> frameCallbacks = mFrameCallbacks;
mFrameCallbacks = new ArrayList<>(frameCallbacks.size());
for (int i = 0, size = frameCallbacks.size(); i < size; i++) {
frameCallbacks.get(i).onAnimationFrame(currentFrameTimeMs);
double currentFrameTimeMs = frameTimeNanos / 1000000.;
if (mSlowAnimationsEnabled) {
currentFrameTimeMs =
mFirstUptime + (currentFrameTimeMs - mFirstUptime) / mAnimationsDragFactor;
}
if (currentFrameTimeMs > lastFrameTimeMs) {
// It is possible for ChoreographerCallback to be executed twice within the same frame
// due to frame drops. If this occurs, the additional callback execution should be ignored.
lastFrameTimeMs = currentFrameTimeMs;
while (!mEventQueue.isEmpty()) {
CopiedEvent copiedEvent = mEventQueue.poll();
handleEvent(
copiedEvent.getTargetTag(), copiedEvent.getEventName(), copiedEvent.getPayload());
}
if (!mFrameCallbacks.isEmpty()) {
List<OnAnimationFrame> frameCallbacks = mFrameCallbacks;
mFrameCallbacks = new ArrayList<>(frameCallbacks.size());
for (int i = 0, size = frameCallbacks.size(); i < size; i++) {
frameCallbacks.get(i).onAnimationFrame(currentFrameTimeMs);
}
}
performOperations();
}
performOperations();
mCallbackPosted.set(false);
if (!mFrameCallbacks.isEmpty() || !mEventQueue.isEmpty()) {
// enqueue next frame
startUpdatingOnAnimationFrame();
}
} finally {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.endSection();
}
}
mCallbackPosted.set(false);
if (!mFrameCallbacks.isEmpty() || !mEventQueue.isEmpty()) {
// enqueue next frame
startUpdatingOnAnimationFrame();
}
// Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
public void enqueueUpdateViewOnNativeThread(
int viewTag, WritableMap nativeProps, boolean trySynchronously) {
if (trySynchronously) {
mTryRunBatchUpdatesSynchronously = true;
}
mOperationsInBatch.add(new NativeUpdateOperation(viewTag, nativeProps));
}
public void configureProps(Set<String> uiPropsSet, Set<String> nativePropsSet) {
uiProps = uiPropsSet;
nativeProps = nativePropsSet;
}
public void postOnAnimation(OnAnimationFrame onAnimationFrame) {
@@ -304,22 +177,32 @@ public class NodesManager implements EventDispatcherListener {
@Override
public void onEventDispatch(Event event) {
if (mNativeProxy == null) {
return;
}
// Events can be dispatched from any thread so we have to make sure handleEvent is run from the
// UI thread.
if (UiThreadUtil.isOnUiThread()) {
handleEvent(event);
performOperations();
} else {
String eventName = mCustomEventNamesResolver.resolveCustomEventName(event.getEventName());
int viewTag = event.getViewTag();
boolean shouldSaveEvent = mNativeProxy.isAnyHandlerWaitingForEvent(eventName, viewTag);
if (shouldSaveEvent) {
mEventQueue.offer(new CopiedEvent(event));
try {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.beginSection("onEventDispatch");
}
if (mNativeProxy == null) {
return;
}
// Events can be dispatched from any thread so we have to make sure handleEvent is run from
// the UI thread.
if (UiThreadUtil.isOnUiThread()) {
handleEvent(event);
performOperations();
} else {
String eventName = mCustomEventNamesResolver.resolveCustomEventName(event.getEventName());
int viewTag = event.getViewTag();
boolean shouldSaveEvent = mNativeProxy.isAnyHandlerWaitingForEvent(eventName, viewTag);
if (shouldSaveEvent) {
mEventQueue.offer(new CopiedEvent(event));
}
startUpdatingOnAnimationFrame();
}
} finally {
if (BuildConfig.REANIMATED_PROFILING) {
Trace.endSection();
}
startUpdatingOnAnimationFrame();
}
}
@@ -343,128 +226,11 @@ public class NodesManager implements EventDispatcherListener {
mEventEmitter.emit(name, body);
}
public void updateProps(int viewTag, Map<String, Object> props) {
// TODO: update PropsNode to use this method instead of its own way of updating props
boolean hasUIProps = false;
boolean hasNativeProps = false;
boolean hasJSProps = false;
JavaOnlyMap newUIProps = new JavaOnlyMap();
WritableMap newJSProps = Arguments.createMap();
WritableMap newNativeProps = Arguments.createMap();
for (Map.Entry<String, Object> entry : props.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (uiProps.contains(key)) {
hasUIProps = true;
addProp(newUIProps, key, value);
} else if (nativeProps.contains(key)) {
hasNativeProps = true;
addProp(newNativeProps, key, value);
} else {
hasJSProps = true;
addProp(newJSProps, key, value);
}
}
if (viewTag != View.NO_ID) {
if (hasUIProps) {
mUIImplementation.synchronouslyUpdateViewOnUIThread(
viewTag, new ReactStylesDiffMap(newUIProps));
}
if (hasNativeProps) {
enqueueUpdateViewOnNativeThread(viewTag, newNativeProps, true);
}
if (hasJSProps) {
WritableMap evt = Arguments.createMap();
evt.putInt("viewTag", viewTag);
evt.putMap("props", newJSProps);
sendEvent("onReanimatedPropsChange", evt);
}
}
}
public void synchronouslyUpdateUIProps(int viewTag, ReadableMap uiProps) {
compatibility.synchronouslyUpdateUIProps(viewTag, uiProps);
}
public String obtainProp(int viewTag, String propName) {
View view = mUIManager.resolveView(viewTag);
String result =
"error: unknown propName " + propName + ", currently supported: opacity, zIndex";
if (propName.equals("opacity")) {
Float opacity = view.getAlpha();
result = Float.toString(opacity);
} else if (propName.equals("zIndex")) {
Float zIndex = view.getElevation();
result = Float.toString(zIndex);
}
return result;
}
private static WritableMap copyReadableMap(ReadableMap map) {
WritableMap copy = Arguments.createMap();
copy.merge(map);
return copy;
}
private static WritableArray copyReadableArray(ReadableArray array) {
WritableArray copy = Arguments.createArray();
for (int i = 0; i < array.size(); i++) {
ReadableType type = array.getType(i);
switch (type) {
case Boolean:
copy.pushBoolean(array.getBoolean(i));
break;
case String:
copy.pushString(array.getString(i));
break;
case Null:
copy.pushNull();
break;
case Number:
copy.pushDouble(array.getDouble(i));
break;
case Map:
copy.pushMap(copyReadableMap(array.getMap(i)));
break;
case Array:
copy.pushArray(copyReadableArray(array.getArray(i)));
break;
default:
throw new IllegalStateException("[Reanimated] Unknown type of ReadableArray.");
}
}
return copy;
}
private static void addProp(WritableMap propMap, String key, Object value) {
if (value == null) {
propMap.putNull(key);
} else if (value instanceof Double) {
propMap.putDouble(key, (Double) value);
} else if (value instanceof Integer) {
propMap.putInt(key, (Integer) value);
} else if (value instanceof Number) {
propMap.putDouble(key, ((Number) value).doubleValue());
} else if (value instanceof Boolean) {
propMap.putBoolean(key, (Boolean) value);
} else if (value instanceof String) {
propMap.putString(key, (String) value);
} else if (value instanceof ReadableArray) {
if (!(value instanceof WritableArray)) {
propMap.putArray(key, copyReadableArray((ReadableArray) value));
} else {
propMap.putArray(key, (ReadableArray) value);
}
} else if (value instanceof ReadableMap) {
if (!(value instanceof WritableMap)) {
propMap.putMap(key, copyReadableMap((ReadableMap) value));
} else {
propMap.putMap(key, (ReadableMap) value);
}
} else {
throw new IllegalStateException("[Reanimated] Unknown type of animated value.");
public void enableSlowAnimations(boolean slowAnimationsEnabled, int animationsDragFactor) {
mSlowAnimationsEnabled = slowAnimationsEnabled;
mAnimationsDragFactor = animationsDragFactor;
if (slowAnimationsEnabled) {
mFirstUptime = SystemClock.uptimeMillis();
}
}
}
@@ -1,55 +1,38 @@
package com.swmansion.reanimated;
import android.util.Log;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.UIBlock;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.UIManagerModuleListener;
import java.util.ArrayList;
import javax.annotation.Nullable;
import com.swmansion.worklets.WorkletsModule;
@ReactModule(name = ReanimatedModule.NAME)
public class ReanimatedModule extends ReactContextBaseJavaModule
implements LifecycleEventListener, UIManagerModuleListener {
public static final String NAME = "ReanimatedModule";
private interface UIThreadOperation {
void execute(NodesManager nodesManager);
}
private ArrayList<UIThreadOperation> mOperations = new ArrayList<>();
private @Nullable NodesManager mNodesManager;
public class ReanimatedModule extends NativeReanimatedModuleSpec implements LifecycleEventListener {
private final NodesManager mNodesManager;
private final WorkletsModule mWorkletsModule;
public ReanimatedModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.assertOnJSQueueThread();
mWorkletsModule = reactContext.getNativeModule(WorkletsModule.class);
mNodesManager = new NodesManager(reactContext, mWorkletsModule);
}
@Override
public void initialize() {
ReactApplicationContext reactCtx = getReactApplicationContext();
UIManagerModule uiManager = reactCtx.getNativeModule(UIManagerModule.class);
reactCtx.addLifecycleEventListener(this);
uiManager.addUIManagerListener(this);
ReactApplicationContext reactContext = getReactApplicationContext();
reactContext.assertOnJSQueueThread();
reactContext.addLifecycleEventListener(this);
}
@Override
public void onHostPause() {
if (mNodesManager != null) {
mNodesManager.onHostPause();
}
mNodesManager.onHostPause();
}
@Override
public void onHostResume() {
if (mNodesManager != null) {
mNodesManager.onHostResume();
}
mNodesManager.onHostResume();
}
@Override
@@ -57,70 +40,33 @@ public class ReanimatedModule extends ReactContextBaseJavaModule
// do nothing
}
@Override
public void willDispatchViewUpdates(final UIManagerModule uiManager) {
if (mOperations.isEmpty()) {
return;
}
final ArrayList<UIThreadOperation> operations = mOperations;
mOperations = new ArrayList<>();
uiManager.addUIBlock(
new UIBlock() {
@Override
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
NodesManager nodesManager = getNodesManager();
for (UIThreadOperation operation : operations) {
operation.execute(nodesManager);
}
}
});
}
@Override
public String getName() {
return NAME;
}
/*package*/
public NodesManager getNodesManager() {
if (mNodesManager == null) {
mNodesManager = new NodesManager(getReactApplicationContext());
}
// This method is called from react-native-gesture-handler.
return mNodesManager;
}
@ReactMethod(isBlockingSynchronousMethod = true)
public void installTurboModule() {
// When debugging in chrome the JS context is not available.
// https://github.com/facebook/react-native/blob/v0.67.0-rc.6/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobCollector.java#L25
Utils.isChromeDebugger = getReactApplicationContext().getJavaScriptContextHolder().get() == 0;
if (!Utils.isChromeDebugger) {
this.getNodesManager().initWithContext(getReactApplicationContext());
} else {
Log.w(
"[REANIMATED]",
"Unable to create Reanimated Native Module. You can ignore this message if you are using Chrome Debugger now.");
}
public boolean installTurboModule() {
getReactApplicationContext().assertOnJSQueueThread();
mNodesManager.getNativeProxy().installJSIBindings();
return true;
}
@ReactMethod
public void addListener(String eventName) {
public void addListener(String ignoredEventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
public void removeListeners(Integer ignoredCount) {
// Keep: Required for RN built in Event Emitter Calls.
}
@Override
public void onCatalystInstanceDestroy() {
super.onCatalystInstanceDestroy();
if (mNodesManager != null) {
mNodesManager.onCatalystInstanceDestroy();
}
public void invalidate() {
super.invalidate();
mNodesManager.invalidate();
ReactApplicationContext reactContext = getReactApplicationContext();
reactContext.removeLifecycleEventListener(this);
}
}
@@ -1,84 +1,58 @@
package com.swmansion.reanimated;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_END;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_START;
import androidx.annotation.NonNull;
import com.facebook.react.BaseReactPackage;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
import com.facebook.react.uimanager.ReanimatedUIManager;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.systrace.Systrace;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class ReanimatedPackage extends TurboReactPackage implements ReactPackage {
@ReactModuleList(
nativeModules = {
ReanimatedModule.class,
})
public class ReanimatedPackage extends BaseReactPackage implements ReactPackage {
@Override
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
if (name.equals(ReanimatedModule.NAME)) {
return new ReanimatedModule(reactContext);
}
if (name.equals(ReanimatedUIManager.NAME)) {
return createUIManager(reactContext);
}
return null;
public NativeModule getModule(
@NonNull String name, @NonNull ReactApplicationContext reactContext) {
return switch (name) {
case ReanimatedModule.NAME -> new ReanimatedModule(reactContext);
default -> null;
};
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
Class<? extends NativeModule>[] moduleList =
new Class[] {
ReanimatedModule.class, ReanimatedUIManager.class,
ReanimatedModule.class,
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
ReactModule reactModule =
Objects.requireNonNull(moduleClass.getAnnotation(ReactModule.class));
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
true,
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.hasConstants(),
reactModule.isCxxModule(),
TurboModule.class.isAssignableFrom(moduleClass)));
true));
}
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
return reactModuleInfoMap;
}
};
}
private UIManagerModule createUIManager(final ReactApplicationContext reactContext) {
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createUIManagerModule");
final ReactInstanceManager reactInstanceManager = getReactInstanceManager(reactContext);
List<ViewManager> viewManagers = reactInstanceManager.getOrCreateViewManagers(reactContext);
int minTimeLeftInFrameForNonBatchedOperationMs = -1;
try {
return ReanimatedUIManagerFactory.create(
reactContext, viewManagers, minTimeLeftInFrameForNonBatchedOperationMs);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_END);
}
return () -> reactModuleInfoMap;
}
/**
@@ -8,8 +8,6 @@ import java.util.Map;
public class Utils {
public static boolean isChromeDebugger = false;
public static Map<String, Integer> processMapping(ReadableMap style) {
ReadableMapKeySetIterator iter = style.keySetIterator();
HashMap<String, Integer> mapping = new HashMap<>();
@@ -46,4 +44,14 @@ public class Utils {
}
return 0;
}
public static Runnable combineRunnables(final Runnable... runnables) {
return new Runnable() {
public void run() {
for (Runnable r : runnables) {
r.run();
}
}
};
}
}
@@ -12,7 +12,8 @@ import com.facebook.react.uimanager.events.RCTEventEmitter;
public class EventHandler implements RCTEventEmitter {
@DoNotStrip private final HybridData mHybridData;
UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
public UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
@DoNotStrip
private EventHandler(HybridData hybridData) {
@@ -17,6 +17,7 @@ public class ReanimatedSensor {
Sensor sensor;
ReanimatedSensorType sensorType;
int interval;
private static final int DEFAULT_INTERVAL = 8;
ReanimatedSensor(
WeakReference<ReactApplicationContext> reactContext,
@@ -30,7 +31,7 @@ public class ReanimatedSensor {
(SensorManager) reactContext.get().getSystemService(reactContext.get().SENSOR_SERVICE);
this.sensorType = sensorType;
if (interval == -1) {
this.interval = SensorManager.SENSOR_DELAY_UI;
this.interval = DEFAULT_INTERVAL;
} else {
this.interval = interval;
}