Skip to content

Commit b2f2902

Browse files
committed
add more logs to ReactViewGroup dispatchDraw and onViewRemoved
1 parent 8393d9f commit b2f2902

File tree

1 file changed

+86
-3
lines changed
  • packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view

1 file changed

+86
-3
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ public void shutdown() {
141141
private @Nullable Set<Integer> mChildrenRemovedWhileTransitioning;
142142
private boolean mPreventClipping;
143143

144+
// debug values that will allow us to track down the source of the NPEs, related task: https://app.asana.com/1/236888843494340/project/1211388770903360/task/1211036733309341
145+
private static final String[] removalTrackedChildrenIDs = {
146+
"native-freeze-screens-container",
147+
"guilds-bar-fast-list",
148+
};
149+
150+
144151
/**
145152
* Creates a new `ReactViewGroup` instance.
146153
*
@@ -641,6 +648,25 @@ public void onViewAdded(View child) {
641648

642649
@Override
643650
public void onViewRemoved(View child) {
651+
try {
652+
Object childNativeId = child.getTag(R.id.view_tag_native_id);
653+
if (nativeId instanceof String) {
654+
String nativeID = (String) nativeId;
655+
boolean matches = java.util.Arrays.stream(removalTrackedChildrenIDs)
656+
.anyMatch(nativeID::equals);
657+
if (matches) {
658+
StringBuilder stackTraceBuilder = new StringBuilder();
659+
for (StackTraceElement element : new Exception().getStackTrace()) {
660+
stackTraceBuilder.append("\n at ").append(element.toString());
661+
}
662+
String stackTrace = stackTraceBuilder.toString();
663+
FLog.e(ReactConstants.TAG, "onViewRemoved of one of the tracked children: " + nativeID + " with stack trace: " + "\n" + stackTrace);
664+
}
665+
}
666+
} catch (Exception e) {
667+
FLog.e(ReactConstants.TAG, "Exception in getting onViewRemoved info: " + e.getMessage());
668+
}
669+
644670
UiThreadUtil.assertOnUiThread();
645671
checkViewClippingTag(child, Boolean.TRUE);
646672
if (!customDrawOrderDisabled()) {
@@ -999,17 +1025,20 @@ protected void dispatchDraw(Canvas canvas) {
9991025
if (mOverflow != Overflow.VISIBLE || getTag(R.id.filter) != null) {
10001026
BackgroundStyleApplicator.clipToPaddingBox(this, canvas);
10011027
}
1002-
1028+
10031029
try {
10041030
super.dispatchDraw(canvas);
10051031
} catch (NullPointerException e) {
1032+
String childrenCountInfo = "children count info: mAllChildrenCount:" + mAllChildrenCount + " getChildCount: " + getChildCount();
1033+
String childrenViewInfo = describeChildren();
10061034
String ancestry = null;
10071035
try {
10081036
ancestry = describeViewAncestry(this);
10091037
} catch (Throwable ignore) {}
1010-
FLog.e(TAG, "NullPointerException in dispatchDraw of " + getClass().getName() + ": " + e.getMessage() + "\n" + ancestry);
1038+
FLog.e(TAG, "NullPointerException in dispatchDraw of " + e.getStackTrace() + getClass().getName() + ": " + e.getMessage() + "\n Failing view ancestry: " + ancestry);
1039+
FLog.e(TAG, "Child info: \n" + childrenCountInfo + "\n" + childrenViewInfo);
10111040
throw new NullPointerException(
1012-
"NullPointerException in dispatchDraw of " + getClass().getName() + ": " + e.getMessage() + "\n" + ancestry);
1041+
"NullPointerException in dispatchDraw of " + getClass().getName() + ": " + e.getMessage() + "\n" + e.getStackTrace());
10131042
}
10141043
}
10151044

@@ -1038,6 +1067,60 @@ private static String describeViewAncestry(View start) {
10381067
return sb.toString();
10391068
}
10401069

1070+
// Helper function to describe children for debugging
1071+
private String describeChildren() {
1072+
final StringBuilder sb = new StringBuilder();
1073+
1074+
sb.append(" Attached children: ");
1075+
int childCount = getChildCount();
1076+
if (childCount == 0) {
1077+
sb.append(" (none)\n");
1078+
} else {
1079+
for (int i = 0; i < childCount; i++) {
1080+
try {
1081+
View child = getChildAt(i);
1082+
if (child == null) {
1083+
sb.append(" [").append(i).append("] NULL CHILD!\n");
1084+
} else {
1085+
Object childNativeId = child.getTag(R.id.view_tag_native_id);
1086+
Object childTestId = child.getTag(R.id.react_test_id);
1087+
sb.append(" [").append(i).append("] ")
1088+
.append(child.getClass().getSimpleName())
1089+
.append(" id=").append(child.getId());
1090+
if (childNativeId != null) sb.append(" nativeID=").append(childNativeId);
1091+
if (childTestId != null) sb.append(" testID=").append(childTestId);
1092+
sb.append(" parent=").append(child.getParent() != null ? "attached" : "DETACHED");
1093+
sb.append('\n');
1094+
}
1095+
} catch (Exception e) {
1096+
sb.append(" [").append(i).append("] ERROR: ").append(e.getMessage()).append('\n');
1097+
}
1098+
}
1099+
}
1100+
1101+
try {
1102+
// If removeClippedSubviews is enabled, show clipped children too
1103+
if (mRemoveClippedSubviews && mAllChildren != null && mAllChildrenCount > childCount) {
1104+
sb.append(" Clipped children:\n");
1105+
for (int i = 0; i < mAllChildrenCount; i++) {
1106+
View child = mAllChildren[i];
1107+
if (child != null && child.getParent() == null) {
1108+
Object childNativeId = child.getTag(R.id.view_tag_native_id);
1109+
sb.append(" [").append(i).append("] ")
1110+
.append(child.getClass().getSimpleName())
1111+
.append(" id=").append(child.getId());
1112+
if (childNativeId != null) sb.append(" nativeID=").append(childNativeId);
1113+
sb.append(" (CLIPPED)\n");
1114+
}
1115+
}
1116+
}
1117+
} catch (Exception e) {
1118+
sb.append(" Error describing clipped children: ").append(e.getMessage()).append('\n');
1119+
}
1120+
1121+
return sb.toString();
1122+
}
1123+
10411124
@Override
10421125
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
10431126
boolean drawWithZ = child.getElevation() > 0;

0 commit comments

Comments
 (0)