@Override public void setRemoveClippedSubviews(boolean removeClippedSubviews) { if (removeClippedSubviews == mRemoveClippedSubviews) { return; } mRemoveClippedSubviews = removeClippedSubviews; if (removeClippedSubviews) { mClippingRect = new Rect(); ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect); mAllChildrenCount = getChildCount(); int initialSize = Math.max(12, mAllChildrenCount); mAllChildren = new View[initialSize]; mChildrenLayoutChangeListener = new ChildrenLayoutChangeListener(this); for (int i = 0; i < mAllChildrenCount; i++) { View child = getChildAt(i); mAllChildren[i] = child; child.addOnLayoutChangeListener(mChildrenLayoutChangeListener); } updateClippingRect(); } else { // Add all clipped views back, deallocate additional arrays, remove layoutChangeListener Assertions.assertNotNull(mClippingRect); Assertions.assertNotNull(mAllChildren); Assertions.assertNotNull(mChildrenLayoutChangeListener); for (int i = 0; i < mAllChildrenCount; i++) { mAllChildren[i].removeOnLayoutChangeListener(mChildrenLayoutChangeListener); } getDrawingRect(mClippingRect); updateClippingToRect(mClippingRect); mAllChildren = null; mClippingRect = null; mAllChildrenCount = 0; mChildrenLayoutChangeListener = null; } }
private void updateSubviewClipStatus(View subview) { if (!mRemoveClippedSubviews || getParent() == null) { return; } Assertions.assertNotNull(mClippingRect); Assertions.assertNotNull(mAllChildren); // do fast check whether intersect state changed sHelperRect.set(subview.getLeft(), subview.getTop(), subview.getRight(), subview.getBottom()); boolean intersects = mClippingRect.intersects( sHelperRect.left, sHelperRect.top, sHelperRect.right, sHelperRect.bottom); // If it was intersecting before, should be attached to the parent boolean oldIntersects = (subview.getParent() != null); if (intersects != oldIntersects) { int clippedSoFar = 0; for (int i = 0; i < mAllChildrenCount; i++) { if (mAllChildren[i] == subview) { updateSubviewClipStatus(mClippingRect, i, clippedSoFar); break; } if (mAllChildren[i].getParent() == null) { clippedSoFar++; } } } }
private void setupReactContext(ReactApplicationContext reactContext) { Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext"); UiThreadUtil.assertOnUiThread(); Assertions.assertCondition(mCurrentReactContext == null); mCurrentReactContext = Assertions.assertNotNull(reactContext); CatalystInstance catalystInstance = Assertions.assertNotNull(reactContext.getCatalystInstance()); catalystInstance.initialize(); mDevSupportManager.onNewReactContextCreated(reactContext); mMemoryPressureRouter.addMemoryPressureListener(catalystInstance); moveReactContextToCurrentLifecycleState(); for (ReactRootView rootView : mAttachedRootViews) { attachMeasuredRootViewToInstance(rootView, catalystInstance); } ReactInstanceEventListener[] listeners = new ReactInstanceEventListener[mReactInstanceEventListeners.size()]; listeners = mReactInstanceEventListeners.toArray(listeners); for (ReactInstanceEventListener listener : listeners) { listener.onReactContextInitialized(reactContext); } Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); }
/** * Instantiates a new {@link ReactInstanceManagerImpl}. Before calling {@code build}, the * following must be called: * * <ul> * <li>{@link #setApplication} * <li>{@link #setJSBundleFile} or {@link #setJSMainModuleName} * </ul> */ public ReactInstanceManager build() { Assertions.assertCondition( mUseDeveloperSupport || mJSBundleFile != null, "JS Bundle File has to be provided when dev support is disabled"); Assertions.assertCondition( mJSMainModuleName != null || mJSBundleFile != null, "Either MainModuleName or JS Bundle File needs to be provided"); if (mUIImplementationProvider == null) { // create default UIImplementationProvider if the provided one is null. mUIImplementationProvider = new UIImplementationProvider(); } return new ReactInstanceManagerImpl( Assertions.assertNotNull( mApplication, "Application property has not been set with this builder"), mJSBundleFile, mJSMainModuleName, mPackages, mUseDeveloperSupport, mBridgeIdleDebugListener, Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"), mUIImplementationProvider, mNativeModuleCallExceptionHandler, mJSCConfig); }
@Override public void dispatch(RCTEventEmitter rctEventEmitter) { TouchesHelper.sendTouchEvent( rctEventEmitter, Assertions.assertNotNull(mTouchEventType), getViewTag(), Assertions.assertNotNull(mMotionEvent)); }
@Override public void updateClippingRect() { if (!mRemoveClippedSubviews) { return; } Assertions.assertNotNull(mClippingRect); Assertions.assertNotNull(mAllChildren); ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect); updateClippingToRect(mClippingRect); }
/*package*/ void addViewWithSubviewClippingEnabled(View child, int index, LayoutParams params) { Assertions.assertCondition(mRemoveClippedSubviews); Assertions.assertNotNull(mClippingRect); Assertions.assertNotNull(mAllChildren); addInArray(child, index); // we add view as "clipped" and then run {@link #updateSubviewClipStatus} to conditionally // attach it int clippedSoFar = 0; for (int i = 0; i < index; i++) { if (mAllChildren[i].getParent() == null) { clippedSoFar++; } } updateSubviewClipStatus(mClippingRect, index, clippedSoFar); child.addOnLayoutChangeListener(mChildrenLayoutChangeListener); }
/** * Destroys this catalyst instance, waiting for any other threads in CatalystQueueConfiguration * (besides the UI thread) to finish running. Must be called from the UI thread so that we can * fully shut down other threads. */ /* package */ void destroy() { UiThreadUtil.assertOnUiThread(); if (mDestroyed) { return; } // TODO: tell all APIs to shut down mDestroyed = true; mJavaRegistry.notifyCatalystInstanceDestroy(); mCatalystQueueConfiguration.destroy(); boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0); if (!wasIdle && !mBridgeIdleListeners.isEmpty()) { for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) { listener.onTransitionToBridgeIdle(); } } if (mTraceListener != null) { Systrace.unregisterListener(mTraceListener); } // We can access the Bridge from any thread now because we know either we are on the JS thread // or the JS thread has finished via CatalystQueueConfiguration#destroy() Assertions.assertNotNull(mBridge).dispose(); }
@Override public void run() { Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "DispatchEventsRunnable"); try { Systrace.endAsyncFlow( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ScheduleDispatchFrameCallback", mHasDispatchScheduledCount); mHasDispatchScheduled = false; mHasDispatchScheduledCount++; Assertions.assertNotNull(mRCTEventEmitter); synchronized (mEventsToDispatchLock) { // We avoid allocating an array and iterator, and "sorting" if we don't need to. // This occurs when the size of mEventsToDispatch is zero or one. if (mEventsToDispatchSize > 1) { Arrays.sort(mEventsToDispatch, 0, mEventsToDispatchSize, EVENT_COMPARATOR); } for (int eventIdx = 0; eventIdx < mEventsToDispatchSize; eventIdx++) { Event event = mEventsToDispatch[eventIdx]; // Event can be null if it has been coalesced into another event. if (event == null) { continue; } Systrace.endAsyncFlow( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID()); event.dispatch(mRCTEventEmitter); event.dispose(); } clearEventsToDispatch(); mEventCookieToLastEventIdx.clear(); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } }
private void addInArray(View child, int index) { View[] children = Assertions.assertNotNull(mAllChildren); final int count = mAllChildrenCount; final int size = children.length; if (index == count) { if (size == count) { mAllChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; System.arraycopy(children, 0, mAllChildren, 0, size); children = mAllChildren; } children[mAllChildrenCount++] = child; } else if (index < count) { if (size == count) { mAllChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; System.arraycopy(children, 0, mAllChildren, 0, index); System.arraycopy(children, index, mAllChildren, index + 1, count - index); children = mAllChildren; } else { System.arraycopy(children, index, children, index + 1, count - index); } children[index] = child; mAllChildrenCount++; } else { throw new IndexOutOfBoundsException("index=" + index + " count=" + count); } }
private void enqueueOnChangeEndpointLongPolling() { Request request = new Request.Builder().url(createOnChangeEndpointUrl()).tag(this).build(); Assertions.assertNotNull(mOnChangePollingClient) .newCall(request) .enqueue( new Callback() { @Override public void onFailure(Request request, IOException e) { if (mOnChangePollingEnabled) { // this runnable is used by onchange endpoint poller to delay subsequent requests // in case // of a failure, so that we don't flood network queue with frequent requests in // case when // dev server is down FLog.d(ReactConstants.TAG, "Error while requesting /onchange endpoint", e); mRestartOnChangePollingHandler.postDelayed( new Runnable() { @Override public void run() { handleOnChangePollingResponse(false); } }, LONG_POLL_FAILURE_DELAY_MS); } } @Override public void onResponse(Response response) throws IOException { handleOnChangePollingResponse(response.code() == 205); } }); }
@Override public void onTraceStopped() { getJSModule( Assertions.assertNotNull(mMainExecutorToken), com.facebook.react.bridge.Systrace.class) .setEnabled(false); }
/*package*/ void removeViewWithSubviewClippingEnabled(View view) { Assertions.assertCondition(mRemoveClippedSubviews); Assertions.assertNotNull(mClippingRect); Assertions.assertNotNull(mAllChildren); view.removeOnLayoutChangeListener(mChildrenLayoutChangeListener); int index = indexOfChildInAllChildren(view); if (mAllChildren[index].getParent() != null) { int clippedSoFar = 0; for (int i = 0; i < index; i++) { if (mAllChildren[i].getParent() == null) { clippedSoFar++; } } super.removeViewsInLayout(index - clippedSoFar, 1); } removeFromArray(index); }
public T get() throws Exception { if (mException != null) { throw mException; } Assertions.assertNotNull(mResult); return mResult; }
/*package*/ void removeAllViewsWithSubviewClippingEnabled() { Assertions.assertCondition(mRemoveClippedSubviews); Assertions.assertNotNull(mAllChildren); for (int i = 0; i < mAllChildrenCount; i++) { mAllChildren[i].removeOnLayoutChangeListener(mChildrenLayoutChangeListener); } removeAllViewsInLayout(); mAllChildrenCount = 0; }
public void loadApp( String appKey, ReactInstanceSpecForTest spec, @Nullable Bundle initialProps, String bundleName, boolean useDevSupport) { final CountDownLatch currentLayoutEvent = mLayoutEvent = new CountDownLatch(1); mBridgeIdleSignaler = new ReactBridgeIdleSignaler(); ReactInstanceManager.Builder builder = ReactTestHelper.getReactTestFactory() .getReactInstanceManagerBuilder() .setApplication(getApplication()) .setBundleAssetName(bundleName) // By not setting a JS module name, we force the bundle to be always loaded from // assets, not the devserver, even if dev mode is enabled (such as when testing // redboxes). // This makes sense because we never run the devserver in tests. // .setJSMainModuleName() .addPackage( spec.getAlternativeReactPackageForTest() != null ? spec.getAlternativeReactPackageForTest() : new MainReactPackage()) .addPackage(new InstanceSpecForTestPackage(spec)) .setUseDeveloperSupport(useDevSupport) .setBridgeIdleDebugListener(mBridgeIdleSignaler) .setInitialLifecycleState(mLifecycleState); mReactInstanceManager = builder.build(); mReactInstanceManager.onResume(this, this); Assertions.assertNotNull(mReactRootView) .getViewTreeObserver() .addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { currentLayoutEvent.countDown(); } }); Assertions.assertNotNull(mReactRootView) .startReactApplication(mReactInstanceManager, appKey, initialProps); }
private void updateClippingToRect(Rect clippingRect) { Assertions.assertNotNull(mAllChildren); int clippedSoFar = 0; for (int i = 0; i < mAllChildrenCount; i++) { updateSubviewClipStatus(clippingRect, i, clippedSoFar); if (mAllChildren[i].getParent() == null) { clippedSoFar++; } } }
/** * @return Themed React context for view with a given {@param reactTag} - in the case of root view * it returns the context from {@link #mRootViewsContext} and all the other cases it gets the * context directly from the view using {@link View#getContext}. */ private ThemedReactContext getReactContextForView(int reactTag) { if (mRootTags.get(reactTag)) { return Assertions.assertNotNull(mRootViewsContext.get(reactTag)); } View view = mTagsToViews.get(reactTag); if (view == null) { throw new JSApplicationIllegalArgumentException("Could not find view with tag " + reactTag); } return (ThemedReactContext) view.getContext(); }
private int indexOfChildInAllChildren(View child) { final int count = mAllChildrenCount; final View[] children = Assertions.assertNotNull(mAllChildren); for (int i = 0; i < count; i++) { if (children[i] == child) { return i; } } return -1; }
private void setupReactContext(ReactApplicationContext reactContext) { UiThreadUtil.assertOnUiThread(); Assertions.assertCondition(mCurrentReactContext == null); mCurrentReactContext = Assertions.assertNotNull(reactContext); CatalystInstance catalystInstance = Assertions.assertNotNull(reactContext.getCatalystInstance()); catalystInstance.initialize(); mDevSupportManager.onNewReactContextCreated(reactContext); mMemoryPressureRouter.onNewReactContextCreated(reactContext); moveReactContextToCurrentLifecycleState(reactContext); for (ReactRootView rootView : mAttachedRootViews) { attachMeasuredRootViewToInstance(rootView, catalystInstance); } for (ReactInstanceEventListener listener : mReactInstanceEventListeners) { listener.onReactContextInitialized(reactContext); } }
// This method also sets the child's mParent to null private void removeFromArray(int index) { final View[] children = Assertions.assertNotNull(mAllChildren); final int count = mAllChildrenCount; if (index == count - 1) { children[--mAllChildrenCount] = null; } else if (index >= 0 && index < count) { System.arraycopy(children, index + 1, children, index, count - index - 1); children[--mAllChildrenCount] = null; } else { throw new IndexOutOfBoundsException(); } }
// This is called from java code, so it won't be stripped anyway, but proguard will rename it, // which this prevents. @DoNotStrip @Override public void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments) { synchronized (mTeardownLock) { if (mDestroyed) { FLog.w(ReactConstants.TAG, "Invoking JS callback after bridge has been destroyed."); return; } incrementPendingJSCalls(); Assertions.assertNotNull(mBridge).invokeCallback(executorToken, callbackID, arguments); } }
/** * This method will give JS the opportunity to consume the back button event. If JS does not * consume the event, mDefaultBackButtonImpl will be invoked at the end of the round trip to JS. */ @Override public void onBackPressed() { UiThreadUtil.assertOnUiThread(); ReactContext reactContext = mCurrentReactContext; if (mCurrentReactContext == null) { // Invoke without round trip to JS. FLog.w(ReactConstants.TAG, "Instance detached from instance manager"); invokeDefaultOnBackPressed(); } else { DeviceEventManagerModule deviceEventManagerModule = Assertions.assertNotNull(reactContext).getNativeModule(DeviceEventManagerModule.class); deviceEventManagerModule.emitHardwareBackPressed(); } }
/** * Handles an updateLayout call. All updateLayout calls are collected and dispatched at the end of * a batch because updateLayout calls to layout-only nodes can necessitate multiple updateLayout * calls for all its children. */ public void handleUpdateLayout(ReactShadowNode node) { if (!ENABLED) { mUIViewOperationQueue.enqueueUpdateLayout( Assertions.assertNotNull(node.getParent()).getReactTag(), node.getReactTag(), node.getScreenX(), node.getScreenY(), node.getScreenWidth(), node.getScreenHeight()); return; } applyLayoutBase(node); }
public CatalystInstance build() { return new CatalystInstance( Assertions.assertNotNull(mCatalystQueueConfigurationSpec), Assertions.assertNotNull(mJSExecutor), Assertions.assertNotNull(mRegistry), Assertions.assertNotNull(mJSModulesConfig), Assertions.assertNotNull(mJSBundleLoader), Assertions.assertNotNull(mNativeModuleCallExceptionHandler)); }
// Because react context is created asynchronously, we may have to wait until it is available. // It's simpler than exposing synchronosition mechanism to notify listener than react context // creation has completed. private ReactContext waitForReactContext() { Assertions.assertNotNull(mReactInstanceManager); try { while (true) { ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); if (reactContext != null) { return reactContext; } Thread.sleep(100); } } catch (InterruptedException e) { throw new RuntimeException(e); } }
@Override public boolean canCoalesce() { // We can coalesce move events but not start/end events. Coalescing move events should probably // append historical move data like MotionEvent batching does. This is left as an exercise for // the reader. switch (Assertions.assertNotNull(mTouchEventType)) { case START: case END: case CANCEL: return false; case MOVE: return true; default: throw new RuntimeException("Unknown touch event type: " + mTouchEventType); } }
/* package */ void callFunction( ExecutorToken executorToken, int moduleId, int methodId, NativeArray arguments, String tracingName) { synchronized (mTeardownLock) { if (mDestroyed) { FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed."); return; } incrementPendingJSCalls(); Assertions.assertNotNull(mBridge) .callFunction(executorToken, moduleId, methodId, arguments, tracingName); } }
@Override public void onNewIntent(Intent intent) { if (mCurrentReactContext == null) { FLog.w(ReactConstants.TAG, "Instance detached from instance manager"); } else { String action = intent.getAction(); Uri uri = intent.getData(); if (Intent.ACTION_VIEW.equals(action) && uri != null) { DeviceEventManagerModule deviceEventManagerModule = Assertions.assertNotNull(mCurrentReactContext) .getNativeModule(DeviceEventManagerModule.class); deviceEventManagerModule.emitNewIntentReceived(uri); } mCurrentReactContext.onNewIntent(mCurrentActivity, intent); } }
private void assertNodeDoesNotNeedCustomLayoutForChildren(ReactShadowNode node) { ViewManager viewManager = Assertions.assertNotNull(mViewManagers.get(node.getViewClass())); ViewGroupManager viewGroupManager; if (viewManager instanceof ViewGroupManager) { viewGroupManager = (ViewGroupManager) viewManager; } else { throw new IllegalViewOperationException( "Trying to use view " + node.getViewClass() + " as a parent, but its Manager doesn't extends ViewGroupManager"); } if (viewGroupManager != null && viewGroupManager.needsCustomLayoutForChildren()) { throw new IllegalViewOperationException( "Trying to measure a view using measureLayout/measureLayoutRelativeToParent relative to" + " an ancestor that requires custom layout for it's children (" + node.getViewClass() + "). Use measure instead."); } }