|
25 | 25 | import android.view.ViewParent; |
26 | 26 | import android.view.ViewStructure; |
27 | 27 | import android.view.animation.Animation; |
| 28 | +import android.util.Log; |
28 | 29 | import androidx.annotation.Nullable; |
29 | 30 | import com.facebook.common.logging.FLog; |
30 | 31 | import com.facebook.infer.annotation.Assertions; |
@@ -141,6 +142,13 @@ public void shutdown() { |
141 | 142 | private @Nullable Set<Integer> mChildrenRemovedWhileTransitioning; |
142 | 143 | private boolean mPreventClipping; |
143 | 144 |
|
| 145 | + // 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 |
| 146 | + private static final String[] removalTrackedChildrenIDs = { |
| 147 | + "native-freeze-screens-container", |
| 148 | + "guilds-bar-fast-list", |
| 149 | + }; |
| 150 | + |
| 151 | + |
144 | 152 | /** |
145 | 153 | * Creates a new `ReactViewGroup` instance. |
146 | 154 | * |
@@ -641,6 +649,21 @@ public void onViewAdded(View child) { |
641 | 649 |
|
642 | 650 | @Override |
643 | 651 | public void onViewRemoved(View child) { |
| 652 | + try { |
| 653 | + Object childNativeId = child.getTag(R.id.view_tag_native_id); |
| 654 | + if (nativeId instanceof String) { |
| 655 | + String nativeID = (String) nativeId; |
| 656 | + boolean matches = java.util.Arrays.stream(removalTrackedChildrenIDs) |
| 657 | + .anyMatch(nativeID::equals); |
| 658 | + if (matches) { |
| 659 | + String stackTrace = android.util.Log.getStackTraceString(new Throwable()); |
| 660 | + FLog.e(ReactConstants.TAG, "onViewRemoved of one of the tracked children: " + nativeID + " with stack trace: " + "\n" + stackTrace); |
| 661 | + } |
| 662 | + } |
| 663 | + } catch (Exception e) { |
| 664 | + FLog.e(ReactConstants.TAG, "Exception in getting onViewRemoved info: " + e.getMessage()); |
| 665 | + } |
| 666 | + |
644 | 667 | UiThreadUtil.assertOnUiThread(); |
645 | 668 | checkViewClippingTag(child, Boolean.TRUE); |
646 | 669 | if (!customDrawOrderDisabled()) { |
@@ -999,17 +1022,21 @@ protected void dispatchDraw(Canvas canvas) { |
999 | 1022 | if (mOverflow != Overflow.VISIBLE || getTag(R.id.filter) != null) { |
1000 | 1023 | BackgroundStyleApplicator.clipToPaddingBox(this, canvas); |
1001 | 1024 | } |
1002 | | - |
| 1025 | + |
1003 | 1026 | try { |
1004 | 1027 | super.dispatchDraw(canvas); |
1005 | 1028 | } catch (NullPointerException e) { |
| 1029 | + String childrenCountInfo = "children count info: mAllChildrenCount:" + mAllChildrenCount + " getChildCount: " + getChildCount(); |
| 1030 | + String childrenViewInfo = describeChildren(); |
1006 | 1031 | String ancestry = null; |
1007 | 1032 | try { |
1008 | 1033 | ancestry = describeViewAncestry(this); |
1009 | 1034 | } catch (Throwable ignore) {} |
1010 | | - FLog.e(TAG, "NullPointerException in dispatchDraw of " + getClass().getName() + ": " + e.getMessage() + "\n" + ancestry); |
| 1035 | + FLog.e(TAG, "NullPointerException in dispatchDraw of " + getClass().getName() + ": " + e.getMessage() + "\n Failing view ancestry: " + ancestry); |
| 1036 | + FLog.e(TAG, "Child info: \n" + childrenCountInfo + "\n" + childrenViewInfo); |
| 1037 | + String caughtErrorStackTrace = android.util.Log.getStackTraceString(e); |
1011 | 1038 | throw new NullPointerException( |
1012 | | - "NullPointerException in dispatchDraw of " + getClass().getName() + ": " + e.getMessage() + "\n" + ancestry); |
| 1039 | + "NullPointerException in dispatchDraw of " + getClass().getName() + ": " + e.getMessage() + "\n" + caughtErrorStackTrace); |
1013 | 1040 | } |
1014 | 1041 | } |
1015 | 1042 |
|
@@ -1038,6 +1065,60 @@ private static String describeViewAncestry(View start) { |
1038 | 1065 | return sb.toString(); |
1039 | 1066 | } |
1040 | 1067 |
|
| 1068 | + // Helper function to describe children for debugging |
| 1069 | + private String describeChildren() { |
| 1070 | + final StringBuilder sb = new StringBuilder(); |
| 1071 | + |
| 1072 | + sb.append(" Attached children: "); |
| 1073 | + int childCount = getChildCount(); |
| 1074 | + if (childCount == 0) { |
| 1075 | + sb.append(" (none)\n"); |
| 1076 | + } else { |
| 1077 | + for (int i = 0; i < childCount; i++) { |
| 1078 | + try { |
| 1079 | + View child = getChildAt(i); |
| 1080 | + if (child == null) { |
| 1081 | + sb.append(" [").append(i).append("] NULL CHILD!\n"); |
| 1082 | + } else { |
| 1083 | + Object childNativeId = child.getTag(R.id.view_tag_native_id); |
| 1084 | + Object childTestId = child.getTag(R.id.react_test_id); |
| 1085 | + sb.append(" [").append(i).append("] ") |
| 1086 | + .append(child.getClass().getSimpleName()) |
| 1087 | + .append(" id=").append(child.getId()); |
| 1088 | + if (childNativeId != null) sb.append(" nativeID=").append(childNativeId); |
| 1089 | + if (childTestId != null) sb.append(" testID=").append(childTestId); |
| 1090 | + sb.append(" parent=").append(child.getParent() != null ? "attached" : "DETACHED"); |
| 1091 | + sb.append('\n'); |
| 1092 | + } |
| 1093 | + } catch (Exception e) { |
| 1094 | + sb.append(" [").append(i).append("] ERROR: ").append(e.getMessage()).append('\n'); |
| 1095 | + } |
| 1096 | + } |
| 1097 | + } |
| 1098 | + |
| 1099 | + try { |
| 1100 | + // If removeClippedSubviews is enabled, show clipped children too |
| 1101 | + if (mRemoveClippedSubviews && mAllChildren != null && mAllChildrenCount > childCount) { |
| 1102 | + sb.append(" Clipped children:\n"); |
| 1103 | + for (int i = 0; i < mAllChildrenCount; i++) { |
| 1104 | + View child = mAllChildren[i]; |
| 1105 | + if (child != null && child.getParent() == null) { |
| 1106 | + Object childNativeId = child.getTag(R.id.view_tag_native_id); |
| 1107 | + sb.append(" [").append(i).append("] ") |
| 1108 | + .append(child.getClass().getSimpleName()) |
| 1109 | + .append(" id=").append(child.getId()); |
| 1110 | + if (childNativeId != null) sb.append(" nativeID=").append(childNativeId); |
| 1111 | + sb.append(" (CLIPPED)\n"); |
| 1112 | + } |
| 1113 | + } |
| 1114 | + } |
| 1115 | + } catch (Exception e) { |
| 1116 | + sb.append(" Error describing clipped children: ").append(e.getMessage()).append('\n'); |
| 1117 | + } |
| 1118 | + |
| 1119 | + return sb.toString(); |
| 1120 | + } |
| 1121 | + |
1041 | 1122 | @Override |
1042 | 1123 | protected boolean drawChild(Canvas canvas, View child, long drawingTime) { |
1043 | 1124 | boolean drawWithZ = child.getElevation() > 0; |
|
0 commit comments