Skip to content

Commit 507c8c4

Browse files
lukmccalltjzelpiaskowyktomekzaw
authored
fix: Break circular reference in NativeProxy and AndroidUIScheduler (#6697)
## Summary Breaks circular reference in `NativeProxy` and `AndroidUIScheduler`. Needs #6671 to work. `NativeProxy` and `AndroidUIScheduler` is a `HybridClass` with a C++ counterpart holding a global reference to the Java object. This structure creates a circular reference between C++ and Java, which the garbage collector cannot clean up. To resolve this issue, I manually removed the reference between C++ and Java by resetting the global ref during the invalidation of the `NativeProxy` and `AndroidUIScheduler`. ## Test plan I tested it with a new project using Expo (SDK 52) and with Fabric enabled. I reloaded the app several times and performed a heap dump to verify that everything was removed correctly. --------- Co-authored-by: Tomasz Żelawski <tzelawski@gmail.com> Co-authored-by: Krzysztof Piaskowy <krzysztof.piaskowy@swmansion.com> Co-authored-by: Tomek Zawadzki <tomekzawadzki98@gmail.com>
1 parent 7ca12fa commit 507c8c4

File tree

4 files changed

+31
-5
lines changed

4 files changed

+31
-5
lines changed

packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ void NativeProxy::registerEventHandler() {
217217
}
218218

219219
void NativeProxy::maybeFlushUIUpdatesQueue() {
220+
// Module might be already destroyed.
221+
if (!javaPart_) {
222+
return;
223+
}
224+
220225
static const auto method = getJniMethod<void()>("maybeFlushUIUpdatesQueue");
221226
method(javaPart_.get());
222227
}

packages/react-native-worklets/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ class UISchedulerWrapper : public UIScheduler {
2525
androidUiScheduler_->cthis()->scheduleTriggerOnUI();
2626
}
2727
}
28-
29-
~UISchedulerWrapper() {}
3028
};
3129

3230
AndroidUIScheduler::AndroidUIScheduler(
@@ -41,6 +39,9 @@ jni::local_ref<AndroidUIScheduler::jhybriddata> AndroidUIScheduler::initHybrid(
4139
}
4240

4341
void AndroidUIScheduler::triggerUI() {
42+
if (!uiScheduler_) {
43+
return;
44+
}
4445
uiScheduler_->triggerUI();
4546
}
4647

@@ -50,10 +51,16 @@ void AndroidUIScheduler::scheduleTriggerOnUI() {
5051
method(javaPart_.get());
5152
}
5253

54+
void AndroidUIScheduler::invalidate() {
55+
javaPart_ = nullptr;
56+
uiScheduler_.reset();
57+
}
58+
5359
void AndroidUIScheduler::registerNatives() {
5460
registerHybrid({
5561
makeNativeMethod("initHybrid", AndroidUIScheduler::initHybrid),
5662
makeNativeMethod("triggerUI", AndroidUIScheduler::triggerUI),
63+
makeNativeMethod("invalidate", AndroidUIScheduler::invalidate),
5764
});
5865
}
5966

packages/react-native-worklets/android/src/main/cpp/worklets/android/AndroidUIScheduler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class AndroidUIScheduler : public jni::HybridClass<AndroidUIScheduler> {
3434

3535
void triggerUI();
3636

37+
void invalidate();
38+
3739
jni::global_ref<AndroidUIScheduler::javaobject> javaPart_;
3840
std::shared_ptr<UIScheduler> uiScheduler_;
3941

packages/react-native-worklets/android/src/worklets/main/com/swmansion/worklets/AndroidUIScheduler.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,15 @@ public class AndroidUIScheduler {
1818

1919
private final Runnable mUIThreadRunnable =
2020
() -> {
21-
if (mActive.get()) {
22-
triggerUI();
21+
/**
22+
* This callback is called on the UI thread, but the module is invalidated on the JS thread.
23+
* Therefore we must synchronize for reloads. Without synchronization the cpp part gets torn
24+
* down while the UI thread is still executing it, leading to crashes.
25+
*/
26+
synchronized (mActive) {
27+
if (mActive.get()) {
28+
triggerUI();
29+
}
2330
}
2431
};
2532

@@ -32,6 +39,8 @@ public AndroidUIScheduler(ReactApplicationContext context) {
3239

3340
public native void triggerUI();
3441

42+
public native void invalidate();
43+
3544
@DoNotStrip
3645
private void scheduleTriggerOnUI() {
3746
UiThreadUtil.runOnUiThread(
@@ -43,6 +52,9 @@ public void runGuarded() {
4352
}
4453

4554
public void deactivate() {
46-
mActive.set(false);
55+
synchronized (mActive) {
56+
mActive.set(false);
57+
invalidate();
58+
}
4759
}
4860
}

0 commit comments

Comments
 (0)