43
43
* shadow node hierarchy that is then mapped to a native view hierarchy.
44
44
*/
45
45
public class UIImplementation {
46
+ // Lock needed in order to fix https://github.com/facebook/react-native/issues/17178#issuecomment-395858863
47
+ protected Object uiImplementationThreadLock = new Object ();
46
48
47
49
protected final EventDispatcher mEventDispatcher ;
48
50
protected final ReactApplicationContext mReactContext ;
@@ -197,23 +199,25 @@ public void updateRootView(
197
199
*/
198
200
public <T extends SizeMonitoringFrameLayout & MeasureSpecProvider > void registerRootView (
199
201
T rootView , int tag , ThemedReactContext context ) {
200
- final ReactShadowNode rootCSSNode = createRootShadowNode ();
201
- rootCSSNode .setReactTag (tag );
202
- rootCSSNode .setThemedContext (context );
203
-
204
- int widthMeasureSpec = rootView .getWidthMeasureSpec ();
205
- int heightMeasureSpec = rootView .getHeightMeasureSpec ();
206
- updateRootView (rootCSSNode , widthMeasureSpec , heightMeasureSpec );
207
-
208
- context .runOnNativeModulesQueueThread (new Runnable () {
209
- @ Override
210
- public void run () {
211
- mShadowNodeRegistry .addRootNode (rootCSSNode );
212
- }
213
- });
202
+ synchronized (uiImplementationThreadLock ) {
203
+ final ReactShadowNode rootCSSNode = createRootShadowNode ();
204
+ rootCSSNode .setReactTag (tag ); // Thread safety needed here
205
+ rootCSSNode .setThemedContext (context );
206
+
207
+ int widthMeasureSpec = rootView .getWidthMeasureSpec ();
208
+ int heightMeasureSpec = rootView .getHeightMeasureSpec ();
209
+ updateRootView (rootCSSNode , widthMeasureSpec , heightMeasureSpec );
210
+
211
+ context .runOnNativeModulesQueueThread (new Runnable () {
212
+ @ Override
213
+ public void run () {
214
+ mShadowNodeRegistry .addRootNode (rootCSSNode );
215
+ }
216
+ });
214
217
215
- // register it within NativeViewHierarchyManager
216
- mOperationsQueue .addRootView (tag , rootView , context );
218
+ // register it within NativeViewHierarchyManager
219
+ mOperationsQueue .addRootView (tag , rootView , context );
220
+ }
217
221
}
218
222
219
223
/**
@@ -228,7 +232,9 @@ public void removeRootView(int rootViewTag) {
228
232
* Unregisters a root node with a given tag from the shadow node registry
229
233
*/
230
234
public void removeRootShadowNode (int rootViewTag ) {
231
- mShadowNodeRegistry .removeRootNode (rootViewTag );
235
+ synchronized (uiImplementationThreadLock ) {
236
+ mShadowNodeRegistry .removeRootNode (rootViewTag ); // Thread safety needed here
237
+ }
232
238
}
233
239
234
240
/**
@@ -279,23 +285,25 @@ public Map<String, Long> getProfiledBatchPerfCounters() {
279
285
* Invoked by React to create a new node with a given tag, class name and properties.
280
286
*/
281
287
public void createView (int tag , String className , int rootViewTag , ReadableMap props ) {
282
- ReactShadowNode cssNode = createShadowNode (className );
283
- ReactShadowNode rootNode = mShadowNodeRegistry .getNode (rootViewTag );
284
- Assertions .assertNotNull (rootNode , "Root node with tag " + rootViewTag + " doesn't exist" );
285
- cssNode .setReactTag (tag );
286
- cssNode .setViewClassName (className );
287
- cssNode .setRootTag (rootNode .getReactTag ());
288
- cssNode .setThemedContext (rootNode .getThemedContext ());
289
-
290
- mShadowNodeRegistry .addNode (cssNode );
288
+ synchronized (uiImplementationThreadLock ) {
289
+ ReactShadowNode cssNode = createShadowNode (className );
290
+ ReactShadowNode rootNode = mShadowNodeRegistry .getNode (rootViewTag );
291
+ Assertions .assertNotNull (rootNode , "Root node with tag " + rootViewTag + " doesn't exist" );
292
+ cssNode .setReactTag (tag ); // Thread safety needed here
293
+ cssNode .setViewClassName (className );
294
+ cssNode .setRootTag (rootNode .getReactTag ());
295
+ cssNode .setThemedContext (rootNode .getThemedContext ());
296
+
297
+ mShadowNodeRegistry .addNode (cssNode );
298
+
299
+ ReactStylesDiffMap styles = null ;
300
+ if (props != null ) {
301
+ styles = new ReactStylesDiffMap (props );
302
+ cssNode .updateProperties (styles );
303
+ }
291
304
292
- ReactStylesDiffMap styles = null ;
293
- if (props != null ) {
294
- styles = new ReactStylesDiffMap (props );
295
- cssNode .updateProperties (styles );
305
+ handleCreateView (cssNode , rootViewTag , styles );
296
306
}
297
-
298
- handleCreateView (cssNode , rootViewTag , styles );
299
307
}
300
308
301
309
protected void handleCreateView (
@@ -363,106 +371,108 @@ public void manageChildren(
363
371
@ Nullable ReadableArray addChildTags ,
364
372
@ Nullable ReadableArray addAtIndices ,
365
373
@ Nullable ReadableArray removeFrom ) {
366
- ReactShadowNode cssNodeToManage = mShadowNodeRegistry .getNode (viewTag );
374
+ synchronized (uiImplementationThreadLock ) {
375
+ ReactShadowNode cssNodeToManage = mShadowNodeRegistry .getNode (viewTag );
367
376
368
- int numToMove = moveFrom == null ? 0 : moveFrom .size ();
369
- int numToAdd = addChildTags == null ? 0 : addChildTags .size ();
370
- int numToRemove = removeFrom == null ? 0 : removeFrom .size ();
377
+ int numToMove = moveFrom == null ? 0 : moveFrom .size ();
378
+ int numToAdd = addChildTags == null ? 0 : addChildTags .size ();
379
+ int numToRemove = removeFrom == null ? 0 : removeFrom .size ();
371
380
372
- if (numToMove != 0 && (moveTo == null || numToMove != moveTo .size ())) {
373
- throw new IllegalViewOperationException ("Size of moveFrom != size of moveTo!" );
374
- }
381
+ if (numToMove != 0 && (moveTo == null || numToMove != moveTo .size ())) {
382
+ throw new IllegalViewOperationException ("Size of moveFrom != size of moveTo!" );
383
+ }
375
384
376
- if (numToAdd != 0 && (addAtIndices == null || numToAdd != addAtIndices .size ())) {
377
- throw new IllegalViewOperationException ("Size of addChildTags != size of addAtIndices!" );
378
- }
385
+ if (numToAdd != 0 && (addAtIndices == null || numToAdd != addAtIndices .size ())) {
386
+ throw new IllegalViewOperationException ("Size of addChildTags != size of addAtIndices!" );
387
+ }
379
388
380
- // We treat moves as an add and a delete
381
- ViewAtIndex [] viewsToAdd = new ViewAtIndex [numToMove + numToAdd ];
382
- int [] indicesToRemove = new int [numToMove + numToRemove ];
383
- int [] tagsToRemove = new int [indicesToRemove .length ];
384
- int [] tagsToDelete = new int [numToRemove ];
385
-
386
- if (numToMove > 0 ) {
387
- Assertions .assertNotNull (moveFrom );
388
- Assertions .assertNotNull (moveTo );
389
- for (int i = 0 ; i < numToMove ; i ++) {
390
- int moveFromIndex = moveFrom .getInt (i );
391
- int tagToMove = cssNodeToManage .getChildAt (moveFromIndex ).getReactTag ();
392
- viewsToAdd [i ] = new ViewAtIndex (
393
- tagToMove ,
394
- moveTo .getInt (i ));
395
- indicesToRemove [i ] = moveFromIndex ;
396
- tagsToRemove [i ] = tagToMove ;
389
+ // We treat moves as an add and a delete
390
+ ViewAtIndex [] viewsToAdd = new ViewAtIndex [numToMove + numToAdd ];
391
+ int [] indicesToRemove = new int [numToMove + numToRemove ];
392
+ int [] tagsToRemove = new int [indicesToRemove .length ];
393
+ int [] tagsToDelete = new int [numToRemove ];
394
+
395
+ if (numToMove > 0 ) {
396
+ Assertions .assertNotNull (moveFrom );
397
+ Assertions .assertNotNull (moveTo );
398
+ for (int i = 0 ; i < numToMove ; i ++) {
399
+ int moveFromIndex = moveFrom .getInt (i );
400
+ int tagToMove = cssNodeToManage .getChildAt (moveFromIndex ).getReactTag ();
401
+ viewsToAdd [i ] = new ViewAtIndex (
402
+ tagToMove ,
403
+ moveTo .getInt (i ));
404
+ indicesToRemove [i ] = moveFromIndex ;
405
+ tagsToRemove [i ] = tagToMove ;
406
+ }
397
407
}
398
- }
399
408
400
- if (numToAdd > 0 ) {
401
- Assertions .assertNotNull (addChildTags );
402
- Assertions .assertNotNull (addAtIndices );
403
- for (int i = 0 ; i < numToAdd ; i ++) {
404
- int viewTagToAdd = addChildTags .getInt (i );
405
- int indexToAddAt = addAtIndices .getInt (i );
406
- viewsToAdd [numToMove + i ] = new ViewAtIndex (viewTagToAdd , indexToAddAt );
409
+ if (numToAdd > 0 ) {
410
+ Assertions .assertNotNull (addChildTags );
411
+ Assertions .assertNotNull (addAtIndices );
412
+ for (int i = 0 ; i < numToAdd ; i ++) {
413
+ int viewTagToAdd = addChildTags .getInt (i );
414
+ int indexToAddAt = addAtIndices .getInt (i );
415
+ viewsToAdd [numToMove + i ] = new ViewAtIndex (viewTagToAdd , indexToAddAt );
416
+ }
407
417
}
408
- }
409
418
410
- if (numToRemove > 0 ) {
411
- Assertions .assertNotNull (removeFrom );
412
- for (int i = 0 ; i < numToRemove ; i ++) {
413
- int indexToRemove = removeFrom .getInt (i );
414
- int tagToRemove = cssNodeToManage .getChildAt (indexToRemove ).getReactTag ();
415
- indicesToRemove [numToMove + i ] = indexToRemove ;
416
- tagsToRemove [numToMove + i ] = tagToRemove ;
417
- tagsToDelete [i ] = tagToRemove ;
419
+ if (numToRemove > 0 ) {
420
+ Assertions .assertNotNull (removeFrom );
421
+ for (int i = 0 ; i < numToRemove ; i ++) {
422
+ int indexToRemove = removeFrom .getInt (i );
423
+ int tagToRemove = cssNodeToManage .getChildAt (indexToRemove ).getReactTag ();
424
+ indicesToRemove [numToMove + i ] = indexToRemove ;
425
+ tagsToRemove [numToMove + i ] = tagToRemove ;
426
+ tagsToDelete [i ] = tagToRemove ;
427
+ }
418
428
}
419
- }
420
429
421
- // NB: moveFrom and removeFrom are both relative to the starting state of the View's children.
422
- // moveTo and addAt are both relative to the final state of the View's children.
423
- //
424
- // 1) Sort the views to add and indices to remove by index
425
- // 2) Iterate the indices being removed from high to low and remove them. Going high to low
426
- // makes sure we remove the correct index when there are multiple to remove.
427
- // 3) Iterate the views being added by index low to high and add them. Like the view removal,
428
- // iteration direction is important to preserve the correct index.
429
-
430
- Arrays .sort (viewsToAdd , ViewAtIndex .COMPARATOR );
431
- Arrays .sort (indicesToRemove );
432
-
433
- // Apply changes to CSSNodeDEPRECATED hierarchy
434
- int lastIndexRemoved = -1 ;
435
- for (int i = indicesToRemove .length - 1 ; i >= 0 ; i --) {
436
- int indexToRemove = indicesToRemove [i ];
437
- if (indexToRemove == lastIndexRemoved ) {
438
- throw new IllegalViewOperationException ("Repeated indices in Removal list for view tag: "
439
- + viewTag );
430
+ // NB: moveFrom and removeFrom are both relative to the starting state of the View's children.
431
+ // moveTo and addAt are both relative to the final state of the View's children.
432
+ //
433
+ // 1) Sort the views to add and indices to remove by index
434
+ // 2) Iterate the indices being removed from high to low and remove them. Going high to low
435
+ // makes sure we remove the correct index when there are multiple to remove.
436
+ // 3) Iterate the views being added by index low to high and add them. Like the view removal,
437
+ // iteration direction is important to preserve the correct index.
438
+
439
+ Arrays .sort (viewsToAdd , ViewAtIndex .COMPARATOR );
440
+ Arrays .sort (indicesToRemove );
441
+
442
+ // Apply changes to CSSNodeDEPRECATED hierarchy
443
+ int lastIndexRemoved = -1 ;
444
+ for (int i = indicesToRemove .length - 1 ; i >= 0 ; i --) {
445
+ int indexToRemove = indicesToRemove [i ];
446
+ if (indexToRemove == lastIndexRemoved ) {
447
+ throw new IllegalViewOperationException ("Repeated indices in Removal list for view tag: "
448
+ + viewTag );
449
+ }
450
+ cssNodeToManage .removeChildAt (indicesToRemove [i ]); // Thread safety needed here
451
+ lastIndexRemoved = indicesToRemove [i ];
440
452
}
441
- cssNodeToManage .removeChildAt (indicesToRemove [i ]);
442
- lastIndexRemoved = indicesToRemove [i ];
443
- }
444
453
445
- for (int i = 0 ; i < viewsToAdd .length ; i ++) {
446
- ViewAtIndex viewAtIndex = viewsToAdd [i ];
447
- ReactShadowNode cssNodeToAdd = mShadowNodeRegistry .getNode (viewAtIndex .mTag );
448
- if (cssNodeToAdd == null ) {
449
- throw new IllegalViewOperationException ("Trying to add unknown view tag: "
450
- + viewAtIndex .mTag );
454
+ for (int i = 0 ; i < viewsToAdd .length ; i ++) {
455
+ ViewAtIndex viewAtIndex = viewsToAdd [i ];
456
+ ReactShadowNode cssNodeToAdd = mShadowNodeRegistry .getNode (viewAtIndex .mTag );
457
+ if (cssNodeToAdd == null ) {
458
+ throw new IllegalViewOperationException ("Trying to add unknown view tag: "
459
+ + viewAtIndex .mTag );
460
+ }
461
+ cssNodeToManage .addChildAt (cssNodeToAdd , viewAtIndex .mIndex );
451
462
}
452
- cssNodeToManage .addChildAt (cssNodeToAdd , viewAtIndex .mIndex );
453
- }
454
463
455
- if (!cssNodeToManage .isVirtual () && !cssNodeToManage .isVirtualAnchor ()) {
456
- mNativeViewHierarchyOptimizer .handleManageChildren (
457
- cssNodeToManage ,
458
- indicesToRemove ,
459
- tagsToRemove ,
460
- viewsToAdd ,
461
- tagsToDelete );
462
- }
464
+ if (!cssNodeToManage .isVirtual () && !cssNodeToManage .isVirtualAnchor ()) {
465
+ mNativeViewHierarchyOptimizer .handleManageChildren (
466
+ cssNodeToManage ,
467
+ indicesToRemove ,
468
+ tagsToRemove ,
469
+ viewsToAdd ,
470
+ tagsToDelete );
471
+ }
463
472
464
- for (int i = 0 ; i < tagsToDelete .length ; i ++) {
465
- removeShadowNode (mShadowNodeRegistry .getNode (tagsToDelete [i ]));
473
+ for (int i = 0 ; i < tagsToDelete .length ; i ++) {
474
+ removeShadowNode (mShadowNodeRegistry .getNode (tagsToDelete [i ]));
475
+ }
466
476
}
467
477
}
468
478
@@ -476,22 +486,23 @@ public void manageChildren(
476
486
public void setChildren (
477
487
int viewTag ,
478
488
ReadableArray childrenTags ) {
479
-
480
- ReactShadowNode cssNodeToManage = mShadowNodeRegistry .getNode (viewTag );
481
-
482
- for (int i = 0 ; i < childrenTags .size (); i ++) {
483
- ReactShadowNode cssNodeToAdd = mShadowNodeRegistry .getNode (childrenTags .getInt (i ));
484
- if (cssNodeToAdd == null ) {
485
- throw new IllegalViewOperationException ("Trying to add unknown view tag: "
486
- + childrenTags .getInt (i ));
489
+ synchronized (uiImplementationThreadLock ) {
490
+ ReactShadowNode cssNodeToManage = mShadowNodeRegistry .getNode (viewTag );
491
+
492
+ for (int i = 0 ; i < childrenTags .size (); i ++) {
493
+ ReactShadowNode cssNodeToAdd = mShadowNodeRegistry .getNode (childrenTags .getInt (i ));
494
+ if (cssNodeToAdd == null ) {
495
+ throw new IllegalViewOperationException ("Trying to add unknown view tag: "
496
+ + childrenTags .getInt (i ));
497
+ }
498
+ cssNodeToManage .addChildAt (cssNodeToAdd , i );
487
499
}
488
- cssNodeToManage .addChildAt (cssNodeToAdd , i );
489
- }
490
500
491
- if (!cssNodeToManage .isVirtual () && !cssNodeToManage .isVirtualAnchor ()) {
492
- mNativeViewHierarchyOptimizer .handleSetChildren (
493
- cssNodeToManage ,
494
- childrenTags );
501
+ if (!cssNodeToManage .isVirtual () && !cssNodeToManage .isVirtualAnchor ()) {
502
+ mNativeViewHierarchyOptimizer .handleSetChildren (
503
+ cssNodeToManage ,
504
+ childrenTags );
505
+ }
495
506
}
496
507
}
497
508
0 commit comments