コード例 #1
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);
    }
コード例 #2
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);
    }
  }
コード例 #3
0
 /**
  * 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();
 }
コード例 #4
0
  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
  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;
  }
コード例 #6
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();
 }
コード例 #7
0
 /*package*/ void removeAllViewsWithSubviewClippingEnabled() {
   Assertions.assertCondition(mRemoveClippedSubviews);
   Assertions.assertNotNull(mAllChildren);
   for (int i = 0; i < mAllChildrenCount; i++) {
     mAllChildren[i].removeOnLayoutChangeListener(mChildrenLayoutChangeListener);
   }
   removeAllViewsInLayout();
   mAllChildrenCount = 0;
 }
コード例 #8
0
  /**
   * 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();
  }
コード例 #9
0
 @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);
   }
 }
コード例 #10
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));
  }
コード例 #11
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();
      }
    }
  }
コード例 #12
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);
 }
コード例 #13
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);
 }
コード例 #14
0
    @Override
    protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
      // TODO(t11687218): Look over all threading
      // Default priority is Process.THREAD_PRIORITY_BACKGROUND which means we'll be put in a cgroup
      // that only has access to a small fraction of CPU time. The priority will be reset after
      // this task finishes:
      // https://android.googlesource.com/platform/frameworks/base/+/d630f105e8bc0021541aacb4dc6498a49048ecea/core/java/android/os/AsyncTask.java#256
      Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);

      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);
      }
    }
コード例 #15
0
 /** Sends the given Event to JS, coalescing eligible events if JS is backed up. */
 public void dispatchEvent(Event event) {
   Assertions.assertCondition(event.isInitialized(), "Dispatched event hasn't been initialized");
   synchronized (mEventsStagingLock) {
     mEventStaging.add(event);
     Systrace.startAsyncFlow(
         Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID());
   }
   if (mRCTEventEmitter != null) {
     // If the host activity is paused, the frame callback may not be currently
     // posted. Ensure that it is so that this event gets delivered promptly.
     mCurrentFrameCallback.maybePostFromNonUI();
   } else {
     // No JS application has started yet, or resumed. This can happen when a ReactRootView is
     // added to view hierarchy, but ReactContext creation has not completed yet. In this case, any
     // touch event dispatch will hit this codepath, and we simply queue them so that they
     // are dispatched once ReactContext creation completes and JS app is running.
   }
 }
コード例 #16
0
  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);
    }
  }
コード例 #17
0
  private void addLayoutOnlyNodeToNonLayoutOnlyNode(
      ReactShadowNode nonLayoutOnlyNode, ReactShadowNode layoutOnlyNode, int index) {
    // Add all of the layout-only node's children to its parent instead
    int currentIndex = index;
    for (int i = 0; i < layoutOnlyNode.getChildCount(); i++) {
      ReactShadowNode childToAdd = layoutOnlyNode.getChildAt(i);
      Assertions.assertCondition(childToAdd.getNativeParent() == null);

      if (childToAdd.isLayoutOnly()) {
        // Adding this layout-only child could result in adding multiple native views
        int childCountBefore = nonLayoutOnlyNode.getNativeChildCount();
        addLayoutOnlyNodeToNonLayoutOnlyNode(nonLayoutOnlyNode, childToAdd, currentIndex);
        int childCountAfter = nonLayoutOnlyNode.getNativeChildCount();
        currentIndex += childCountAfter - childCountBefore;
      } else {
        addNonLayoutNodeToNonLayoutNode(nonLayoutOnlyNode, childToAdd, currentIndex);
        currentIndex++;
      }
    }
  }
コード例 #18
0
  private void transitionLayoutOnlyViewToNativeView(
      ReactShadowNode node, @Nullable CatalystStylesDiffMap props) {
    ReactShadowNode parent = node.getParent();
    if (parent == null) {
      node.setIsLayoutOnly(false);
      return;
    }

    // First, remove the node from its parent. This causes the parent to update its native children
    // count. The removeNodeFromParent call will cause all the view's children to be detached from
    // their native parent.
    int childIndex = parent.indexOf(node);
    parent.removeChildAt(childIndex);
    removeNodeFromParent(node, false);

    node.setIsLayoutOnly(false);

    // Create the view since it doesn't exist in the native hierarchy yet
    mUIViewOperationQueue.enqueueCreateView(
        node.getRootNode().getReactTag(), node.getReactTag(), node.getViewClass(), props);

    // Add the node and all its children as if we are adding a new nodes
    parent.addChildAt(node, childIndex);
    addNodeToNode(parent, node, childIndex);
    for (int i = 0; i < node.getChildCount(); i++) {
      addNodeToNode(node, node.getChildAt(i), i);
    }

    // Update layouts since the children of the node were offset by its x/y position previously.
    // Bit of a hack: we need to update the layout of this node's children now that it's no longer
    // layout-only, but we may still receive more layout updates at the end of this batch that we
    // don't want to ignore.
    Assertions.assertCondition(mTagsWithLayoutVisited.size() == 0);
    applyLayoutBase(node);
    for (int i = 0; i < node.getChildCount(); i++) {
      applyLayoutBase(node.getChildAt(i));
    }
    mTagsWithLayoutVisited.clear();
  }
コード例 #19
0
  private CatalystInstance(
      final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry registry,
      final JavaScriptModulesConfig jsModulesConfig,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    mCatalystQueueConfiguration =
        CatalystQueueConfiguration.create(
            catalystQueueConfigurationSpec, new NativeExceptionHandler());
    mBridgeIdleListeners = new CopyOnWriteArrayList<>();
    mJavaRegistry = registry;
    mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstance.this, jsModulesConfig);
    mJSBundleLoader = jsBundleLoader;
    mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
    mTraceListener = new JSProfilerTraceListener();
    Systrace.registerListener(mTraceListener);

    final CountDownLatch initLatch = new CountDownLatch(1);
    mCatalystQueueConfiguration
        .getJSQueueThread()
        .runOnQueue(
            new Runnable() {
              @Override
              public void run() {
                initializeBridge(jsExecutor, jsModulesConfig);
                initLatch.countDown();
              }
            });

    try {
      Assertions.assertCondition(
          initLatch.await(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS),
          "Timed out waiting for bridge to initialize!");
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }