Skip to content

Commit c86f396

Browse files
committed
fix(new arch): change measure function to measure native view hierarchy on UI thread
1 parent d277e61 commit c86f396

File tree

11 files changed

+147
-9
lines changed

11 files changed

+147
-9
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.facebook.common.logging.FLog;
3333
import com.facebook.infer.annotation.ThreadConfined;
3434
import com.facebook.proguard.annotations.DoNotStripAny;
35+
import com.facebook.react.bridge.Callback;
3536
import com.facebook.react.bridge.ColorPropConverter;
3637
import com.facebook.react.bridge.GuardedRunnable;
3738
import com.facebook.react.bridge.LifecycleEventListener;
@@ -1183,6 +1184,27 @@ public String toString() {
11831184
});
11841185
}
11851186

1187+
public void measure(int surfaceId, int reactTag, final Callback callback) {
1188+
mMountItemDispatcher.addMountItem(
1189+
new MountItem() {
1190+
@Override
1191+
public void execute(@NonNull MountingManager mountingManager) {
1192+
mMountingManager.measure(surfaceId, reactTag, callback);
1193+
}
1194+
1195+
@Override
1196+
public int getSurfaceId() {
1197+
return surfaceId;
1198+
}
1199+
1200+
@NonNull
1201+
@Override
1202+
public String toString() {
1203+
return "MEASURE_VIEW";
1204+
}
1205+
});
1206+
}
1207+
11861208
@Override
11871209
public void profileNextBatch() {
11881210
// TODO T31905686: Remove this method and add support for multi-threading performance counters

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@
1010
import static com.facebook.infer.annotation.ThreadConfined.ANY;
1111
import static com.facebook.infer.annotation.ThreadConfined.UI;
1212

13+
import android.graphics.Matrix;
14+
import android.graphics.RectF;
15+
import android.util.Log;
1316
import android.view.View;
17+
import android.view.ViewParent;
18+
1419
import androidx.annotation.AnyThread;
1520
import androidx.annotation.NonNull;
1621
import androidx.annotation.Nullable;
1722
import androidx.annotation.UiThread;
1823
import com.facebook.common.logging.FLog;
1924
import com.facebook.infer.annotation.ThreadConfined;
25+
import com.facebook.react.bridge.Callback;
2026
import com.facebook.react.bridge.ReactContext;
2127
import com.facebook.react.bridge.ReactSoftExceptionLogger;
2228
import com.facebook.react.bridge.ReadableArray;
@@ -30,12 +36,16 @@
3036
import com.facebook.react.fabric.events.EventEmitterWrapper;
3137
import com.facebook.react.fabric.mounting.mountitems.MountItem;
3238
import com.facebook.react.touch.JSResponderHandler;
39+
import com.facebook.react.uimanager.PixelUtil;
3340
import com.facebook.react.uimanager.RootViewManager;
41+
import com.facebook.react.uimanager.RootViewUtil;
3442
import com.facebook.react.uimanager.ThemedReactContext;
3543
import com.facebook.react.uimanager.ViewManagerRegistry;
3644
import com.facebook.react.uimanager.common.ViewUtil;
3745
import com.facebook.react.uimanager.events.EventCategoryDef;
3846
import com.facebook.yoga.YogaMeasureMode;
47+
48+
import java.util.Arrays;
3949
import java.util.Map;
4050
import java.util.Queue;
4151
import java.util.concurrent.ConcurrentHashMap;
@@ -472,4 +482,65 @@ public void enqueuePendingEvent(
472482
? getSurfaceManagerForView(reactTag)
473483
: getSurfaceManager(surfaceId));
474484
}
485+
486+
public void measure(int surfaceId, int reactTag, final Callback callback) {
487+
UiThreadUtil.assertOnUiThread();
488+
SurfaceMountingManager smm = getSurfaceMountingManager(surfaceId, reactTag);
489+
View view = smm.getView(reactTag);
490+
int[] mMeasureBuffer = new int[4];
491+
measure(view, mMeasureBuffer);
492+
Log.d("HannoDebug", "Live measured: " + Arrays.toString(mMeasureBuffer));
493+
494+
float x = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]);
495+
float y = PixelUtil.toDIPFromPixel(mMeasureBuffer[1]);
496+
float width = PixelUtil.toDIPFromPixel(mMeasureBuffer[2]);
497+
float height = PixelUtil.toDIPFromPixel(mMeasureBuffer[3]);
498+
callback.invoke(0, 0, width, height, x, y);
499+
}
500+
501+
public synchronized void measure(View v, int[] outputBuffer) {
502+
View rootView = (View) RootViewUtil.getRootView(v);
503+
computeBoundingBox(rootView, outputBuffer);
504+
int rootX = outputBuffer[0];
505+
int rootY = outputBuffer[1];
506+
computeBoundingBox(v, outputBuffer);
507+
outputBuffer[0] -= rootX;
508+
outputBuffer[1] -= rootY;
509+
}
510+
511+
private void computeBoundingBox(View view, int[] outputBuffer) {
512+
RectF mBoundingBox = new RectF();
513+
mBoundingBox.set(0, 0, view.getWidth(), view.getHeight());
514+
mapRectFromViewToWindowCoords(view, mBoundingBox);
515+
516+
outputBuffer[0] = Math.round(mBoundingBox.left);
517+
outputBuffer[1] = Math.round(mBoundingBox.top);
518+
outputBuffer[2] = Math.round(mBoundingBox.right - mBoundingBox.left);
519+
outputBuffer[3] = Math.round(mBoundingBox.bottom - mBoundingBox.top);
520+
}
521+
522+
private void mapRectFromViewToWindowCoords(View view, RectF rect) {
523+
Matrix matrix = view.getMatrix();
524+
if (!matrix.isIdentity()) {
525+
matrix.mapRect(rect);
526+
}
527+
528+
rect.offset(view.getLeft(), view.getTop());
529+
530+
ViewParent parent = view.getParent();
531+
while (parent instanceof View) {
532+
View parentView = (View) parent;
533+
534+
rect.offset(-parentView.getScrollX(), -parentView.getScrollY());
535+
536+
matrix = parentView.getMatrix();
537+
if (!matrix.isIdentity()) {
538+
matrix.mapRect(rect);
539+
}
540+
541+
rect.offset(parentView.getLeft(), parentView.getTop());
542+
543+
parent = parentView.getParent();
544+
}
545+
}
475546
}

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include <react/renderer/mounting/ShadowView.h>
2222
#include <react/renderer/mounting/ShadowViewMutation.h>
2323

24+
#include <react/jni/JCallback.h>
25+
//#include <react/bridging/Function.h>
26+
2427
#include <fbjni/fbjni.h>
2528
#include <glog/logging.h>
2629

@@ -1074,4 +1077,14 @@ void FabricMountingManager::onAllAnimationsComplete() {
10741077
allAnimationsCompleteJNI(javaUIManager_);
10751078
}
10761079

1080+
void FabricMountingManager::measure(const facebook::react::ShadowView& shadowView, std::function<void(folly::dynamic)> jsCallback) {
1081+
static auto measureJNI =
1082+
JFabricUIManager::javaClassStatic()->getMethod<void(jint, jint, jni::alias_ref<JCallback>)>(
1083+
"measure");
1084+
1085+
auto javaCallback = JCxxCallbackImpl::newObjectCxxArgs(jsCallback);
1086+
1087+
measureJNI(javaUIManager_, shadowView.surfaceId, shadowView.tag, javaCallback);
1088+
}
1089+
10771090
} // namespace facebook::react

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class FabricMountingManager final {
6060

6161
void onAllAnimationsComplete();
6262

63+
void measure(const ShadowView& shadowView, std::function<void(folly::dynamic)> callback);
64+
6365
private:
6466
bool isOnMainThread();
6567

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,14 @@ void FabricUIManagerBinding::schedulerDidSetIsJSResponder(
611611
shadowView, isJSResponder, blockNativeResponder);
612612
}
613613

614+
void FabricUIManagerBinding::schedulerMeasure(const ShadowView& shadowView, std::function<void(folly::dynamic)> jsCallback) {
615+
auto mountingManager = getMountingManager("schedulerMeasure");
616+
if (!mountingManager) {
617+
return;
618+
}
619+
mountingManager->measure(shadowView, jsCallback);
620+
};
621+
614622
void FabricUIManagerBinding::onAnimationStarted() {
615623
auto mountingManager = getMountingManager("onAnimationStarted");
616624
if (!mountingManager) {

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class FabricUIManagerBinding : public jni::HybridClass<FabricUIManagerBinding>,
120120
bool isJSResponder,
121121
bool blockNativeResponder) override;
122122

123+
void schedulerMeasure(const ShadowView& shadowView, std::function<void(folly::dynamic)> jsCallback) override;
124+
123125
void setPixelDensity(float pointScaleFactor);
124126

125127
void driveCxxAnimations();

packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,13 @@ void Scheduler::uiManagerDidSendAccessibilityEvent(
312312
}
313313
}
314314

315+
void Scheduler::uiManagerMeasure(const ShadowNode::Shared& shadowNode, std::function<void(folly::dynamic)> jsCallback) {
316+
if (delegate_ != nullptr) {
317+
auto shadowView = ShadowView(*shadowNode);
318+
delegate_->schedulerMeasure(shadowView, jsCallback);
319+
}
320+
};
321+
315322
/*
316323
* Set JS responder for a view.
317324
*/

packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class Scheduler final : public UIManagerDelegate {
9595
const ShadowNode::Shared& shadowNode,
9696
bool isJSResponder,
9797
bool blockNativeResponder) override;
98+
void uiManagerMeasure(const ShadowNode::Shared& shadowNode, std::function<void(folly::dynamic)> jsCallback) override;
9899

99100
#pragma mark - ContextContainer
100101
ContextContainer::Shared getContextContainer() const;

packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class SchedulerDelegate {
6464
bool isJSResponder,
6565
bool blockNativeResponder) = 0;
6666

67+
virtual void schedulerMeasure(const ShadowView& shadowView, std::function<void(folly::dynamic)> jsCallback) = 0;
68+
6769
virtual ~SchedulerDelegate() noexcept = default;
6870
};
6971

packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -628,16 +628,24 @@ jsi::Value UIManagerBinding::get(
628628
return jsi::Value::undefined();
629629
}
630630

631-
auto measureRect = dom::measure(currentRevision, *shadowNode);
631+
auto sharedCallback = std::make_shared<jsi::Function>(std::move(callbackFunction));
632+
auto runtimeExecutor = uiManager->runtimeExecutor_;
633+
std::function<void(folly::dynamic)> jsCallback = [sharedCallback, runtimeExecutor](folly::dynamic args) {
634+
// Schedule call on JS
635+
runtimeExecutor([sharedCallback, args](jsi::Runtime& jsRuntime) {
636+
// Invoke the actual callback we got from JS
637+
sharedCallback->call(jsRuntime, {
638+
jsi::Value{jsRuntime, args.at(0).getDouble()},
639+
jsi::Value{jsRuntime, args.at(1).getDouble()},
640+
jsi::Value{jsRuntime, args.at(2).getDouble()},
641+
jsi::Value{jsRuntime, args.at(3).getDouble()},
642+
jsi::Value{jsRuntime, args.at(4).getDouble()},
643+
jsi::Value{jsRuntime, args.at(5).getDouble()},
644+
});
645+
});
646+
};
647+
uiManager->getDelegate()->uiManagerMeasure(shadowNode, std::move(jsCallback));
632648

633-
callbackFunction.call(
634-
runtime,
635-
{jsi::Value{runtime, measureRect.x},
636-
jsi::Value{runtime, measureRect.y},
637-
jsi::Value{runtime, measureRect.width},
638-
jsi::Value{runtime, measureRect.height},
639-
jsi::Value{runtime, measureRect.pageX},
640-
jsi::Value{runtime, measureRect.pageY}});
641649
return jsi::Value::undefined();
642650
});
643651
}

packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class UIManagerDelegate {
4141
const std::string& commandName,
4242
const folly::dynamic& args) = 0;
4343

44+
virtual void uiManagerMeasure(const ShadowNode::Shared& shadowNode, std::function<void(folly::dynamic)> jsCallback) = 0;
45+
4446
/*
4547
* Called when UIManager wants to dispatch some accessibility event
4648
* to the mounting layer. eventType is platform-specific and not all

0 commit comments

Comments
 (0)