예제 #1
0
  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++;
        }
      }
    }
  }
예제 #2
0
 @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;
   }
 }
예제 #3
0
    /**
     * 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);
    }
  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);
  }
예제 #5
0
 @Override
 public void dispatch(RCTEventEmitter rctEventEmitter) {
   TouchesHelper.sendTouchEvent(
       rctEventEmitter,
       Assertions.assertNotNull(mTouchEventType),
       getViewTag(),
       Assertions.assertNotNull(mMotionEvent));
 }
예제 #6
0
 /*package*/ void removeAllViewsWithSubviewClippingEnabled() {
   Assertions.assertCondition(mRemoveClippedSubviews);
   Assertions.assertNotNull(mAllChildren);
   for (int i = 0; i < mAllChildrenCount; i++) {
     mAllChildren[i].removeOnLayoutChangeListener(mChildrenLayoutChangeListener);
   }
   removeAllViewsInLayout();
   mAllChildrenCount = 0;
 }
예제 #7
0
  @Override
  public void updateClippingRect() {
    if (!mRemoveClippedSubviews) {
      return;
    }

    Assertions.assertNotNull(mClippingRect);
    Assertions.assertNotNull(mAllChildren);

    ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect);
    updateClippingToRect(mClippingRect);
  }
예제 #8
0
 /*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);
 }
  private ReactBridge initializeBridge(
      JavaScriptExecutor jsExecutor, JavaScriptModulesConfig jsModulesConfig) {
    mReactQueueConfiguration.getJSQueueThread().assertIsOnThread();
    Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");

    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
    ReactBridge bridge;
    try {
      bridge =
          new ReactBridge(
              jsExecutor,
              new NativeModulesReactCallback(),
              mReactQueueConfiguration.getNativeModulesQueueThread());
      mMainExecutorToken = bridge.getMainExecutorToken();
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
    try {
      bridge.setGlobalVariable(
          "__fbBatchedBridgeConfig",
          buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
      bridge.setGlobalVariable(
          "__RCTProfileIsProfiling",
          Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    mJavaRegistry.notifyReactBridgeInitialized(bridge);
    return bridge;
  }
 @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);
   }
 }
 @Override
 public void onTraceStopped() {
   getJSModule(
           Assertions.assertNotNull(mMainExecutorToken),
           com.facebook.react.bridge.Systrace.class)
       .setEnabled(false);
 }
예제 #12
0
  /**
   * 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();
  }
예제 #13
0
 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);
              }
            });
  }
예제 #15
0
  public void runJSBundle() {
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "CatalystInstance_runJSBundle");

    try {
      final CountDownLatch initLatch = new CountDownLatch(1);
      mCatalystQueueConfiguration
          .getJSQueueThread()
          .runOnQueue(
              new Runnable() {
                @Override
                public void run() {
                  Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!");
                  mJSBundleHasLoaded = true;

                  incrementPendingJSCalls();

                  mJSBundleLoader.loadScript(mBridge);

                  initLatch.countDown();
                }
              });
      Assertions.assertCondition(
          initLatch.await(LOAD_JS_BUNDLE_TIMEOUT_MS, TimeUnit.MILLISECONDS),
          "Timed out loading JS!");
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }
 /**
  * Recreate the react application and context. This should be called if configuration has changed
  * or the developer has requested the app to be reloaded. It should only be called after an
  * initial call to createReactContextInBackground.
  *
  * <p>Called from UI thread.
  */
 public void recreateReactContextInBackground() {
   Assertions.assertCondition(
       mHasStartedCreatingInitialContext,
       "recreateReactContextInBackground should only be called after the initial "
           + "createReactContextInBackground call.");
   recreateReactContextInBackgroundInner();
 }
예제 #17
0
 /*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);
 }
예제 #18
0
 /** Initialize all the native modules */
 @VisibleForTesting
 public void initialize() {
   UiThreadUtil.assertOnUiThread();
   Assertions.assertCondition(
       !mInitialized, "This catalyst instance has already been initialized");
   mInitialized = true;
   mJavaRegistry.notifyCatalystInstanceInitialized();
 }
    public T get() throws Exception {
      if (mException != null) {
        throw mException;
      }

      Assertions.assertNotNull(mResult);

      return mResult;
    }
  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);
  }
예제 #21
0
 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;
 }
 /**
  * @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();
 }
예제 #23
0
 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++;
     }
   }
 }
 @Override
 protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
   Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
   try {
     JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
     return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
   } catch (Exception e) {
     // Pass exception to onPostExecute() so it can be handled on the main thread
     return Result.of(e);
   }
 }
  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);
    }
  }
  /**
   * Trigger react context initialization asynchronously in a background async task. This enables
   * applications to pre-load the application JS, and execute global code before {@link
   * ReactRootView} is available and measured. This should only be called the first time the
   * application is set up, which is enforced to keep developers from accidentally creating their
   * application multiple times without realizing it.
   *
   * <p>Called from UI thread.
   */
  @Override
  public void createReactContextInBackground() {
    Assertions.assertCondition(
        !mHasStartedCreatingInitialContext,
        "createReactContextInBackground should only be called when creating the react "
            + "application for the first time. When reloading JS, e.g. from a new file, explicitly"
            + "use recreateReactContextInBackground");

    mHasStartedCreatingInitialContext = true;
    recreateReactContextInBackgroundInner();
  }
예제 #27
0
 // 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();
   }
 }
예제 #28
0
  private void decrementPendingJSCalls() {
    int newPendingCalls = mPendingJSCalls.decrementAndGet();
    Assertions.assertCondition(newPendingCalls >= 0);
    boolean isNowIdle = newPendingCalls == 0;
    Systrace.traceCounter(
        Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, mJsPendingCallsTitleForTrace, newPendingCalls);

    if (isNowIdle && !mBridgeIdleListeners.isEmpty()) {
      for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
        listener.onTransitionToBridgeIdle();
      }
    }
  }
예제 #29
0
  private void initializeBridge(
      JavaScriptExecutor jsExecutor, JavaScriptModulesConfig jsModulesConfig) {
    mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
    Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");

    mBridge =
        new ReactBridge(
            jsExecutor,
            new NativeModulesReactCallback(),
            mCatalystQueueConfiguration.getNativeModulesQueueThread());
    mBridge.setGlobalVariable(
        "__fbBatchedBridgeConfig", buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
  }
 /**
  * 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();
   }
 }