private void recreateReactContextInBackgroundInner() { UiThreadUtil.assertOnUiThread(); if (mUseDeveloperSupport && mJSMainModuleName != null) { if (mDevSupportManager.hasUpToDateJSBundleInCache()) { // If there is a up-to-date bundle downloaded from server, always use that onJSBundleLoadedFromServer(); } else if (mJSBundleFile == null) { mDevSupportManager.handleReloadJS(); } else { mDevSupportManager.isPackagerRunning( new DevServerHelper.PackagerStatusCallback() { @Override public void onPackagerStatusFetched(final boolean packagerIsRunning) { UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { if (packagerIsRunning) { mDevSupportManager.handleReloadJS(); } else { recreateReactContextInBackgroundFromBundleFile(); } } }); } }); } return; } recreateReactContextInBackgroundFromBundleFile(); }
@Override public void doFrame(long frameTimeNanos) { UiThreadUtil.assertOnUiThread(); if (mShouldStop) { mIsPosted = false; } else { post(); } Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ScheduleDispatchFrameCallback"); try { moveStagedEventsToDispatchQueue(); if (mEventsToDispatchSize > 0 && !mHasDispatchScheduled) { mHasDispatchScheduled = true; Systrace.startAsyncFlow( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ScheduleDispatchFrameCallback", mHasDispatchScheduledCount); mReactContext.runOnJSQueueThread(mDispatchEventsRunnable); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } }
@Override public void onHostResume() { UiThreadUtil.assertOnUiThread(); if (mRCTEventEmitter == null) { mRCTEventEmitter = mReactContext.getJSModule(RCTEventEmitter.class); } mCurrentFrameCallback.maybePost(); }
/** * Detach given {@param rootView} from current catalyst instance. It's safe to call this method * multiple times on the same {@param rootView} - in that case view will be detached with the * first call. */ @Override public void detachRootView(ReactRootView rootView) { UiThreadUtil.assertOnUiThread(); if (mAttachedRootViews.remove(rootView)) { if (mCurrentReactContext != null && mCurrentReactContext.hasActiveCatalystInstance()) { detachViewFromInstance(rootView, mCurrentReactContext.getCatalystInstance()); } } }
public void removeRootView(int rootViewTag) { UiThreadUtil.assertOnUiThread(); if (!mRootTags.get(rootViewTag)) { SoftAssertions.assertUnreachable( "View with tag " + rootViewTag + " is not registered as a root view"); } View rootView = mTagsToViews.get(rootViewTag); dropView(rootView); mRootTags.delete(rootViewTag); mRootViewsContext.remove(rootViewTag); }
/** * Attach given {@param rootView} to a catalyst instance manager and start JS application using JS * module provided by {@link ReactRootView#getJSModuleName}. If the react context is currently * being (re)-created, or if react context has not been created yet, the JS application associated * with the provided root view will be started asynchronously, i.e this method won't block. This * view will then be tracked by this manager and in case of catalyst instance restart it will be * re-attached. */ @Override public void attachMeasuredRootView(ReactRootView rootView) { UiThreadUtil.assertOnUiThread(); mAttachedRootViews.add(rootView); // If react context is being created in the background, JS application will be started // automatically when creation completes, as root view is part of the attached root view list. if (mReactContextInitAsyncTask == null && mCurrentReactContext != null) { attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance()); } }
/** * Returns true on success, false on failure. If successful, after calling, output buffer will be * {x, y, width, height}. */ public void measure(int tag, int[] outputBuffer) { UiThreadUtil.assertOnUiThread(); View v = mTagsToViews.get(tag); if (v == null) { throw new NoSuchNativeViewException("No native view for " + tag + " currently exists"); } // Puts x/y in outputBuffer[0]/[1] v.getLocationOnScreen(outputBuffer); outputBuffer[2] = v.getWidth(); outputBuffer[3] = v.getHeight(); }
private void tearDownReactContext(ReactContext reactContext) { UiThreadUtil.assertOnUiThread(); if (mLifecycleState == LifecycleState.RESUMED) { reactContext.onPause(); } for (ReactRootView rootView : mAttachedRootViews) { detachViewFromInstance(rootView, reactContext.getCatalystInstance()); } reactContext.onDestroy(); mDevSupportManager.onReactInstanceDestroyed(reactContext); mMemoryPressureRouter.onReactInstanceDestroyed(); }
/** * 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(); } }
public void updateViewExtraData(int tag, Object extraData) { UiThreadUtil.assertOnUiThread(); ViewManager viewManager = mTagsToViewManagers.get(tag); if (viewManager == null) { throw new IllegalViewOperationException("ViewManager for tag " + tag + " could not be found"); } View viewToUpdate = mTagsToViews.get(tag); if (viewToUpdate == null) { throw new IllegalViewOperationException( "Trying to update view with tag " + tag + " which " + "doesn't exist"); } viewManager.updateExtraData(viewToUpdate, extraData); }
public void updateProperties(int tag, CatalystStylesDiffMap props) { UiThreadUtil.assertOnUiThread(); ViewManager viewManager = mTagsToViewManagers.get(tag); if (viewManager == null) { throw new IllegalViewOperationException("ViewManager for tag " + tag + " could not be found"); } View viewToUpdate = mTagsToViews.get(tag); if (viewToUpdate == null) { throw new IllegalViewOperationException( "Trying to update view with tag " + tag + " which doesn't exist"); } viewManager.updateProperties(viewToUpdate, props); }
/** * See {@link UIManagerModule#addMeasuredRootView}. * * <p>Must be called from the UI thread. */ public void addRootView( int tag, SizeMonitoringFrameLayout view, ThemedReactContext themedContext) { UiThreadUtil.assertOnUiThread(); if (view.getId() != View.NO_ID) { throw new IllegalViewOperationException( "Trying to add a root view with an explicit id already set. React Native uses " + "the id field to track react tags and will overwrite this field. If that is fine, " + "explicitly overwrite the id field to View.NO_ID before calling addMeasuredRootView."); } mTagsToViews.put(tag, view); mTagsToViewManagers.put(tag, mRootViewManager); mRootTags.put(tag, true); mRootViewsContext.put(tag, themedContext); view.setId(tag); }
private void recreateReactContextInBackground( JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader) { UiThreadUtil.assertOnUiThread(); ReactContextInitParams initParams = new ReactContextInitParams(jsExecutorFactory, jsBundleLoader); if (mReactContextInitAsyncTask == null) { // No background task to create react context is currently running, create and execute one. mReactContextInitAsyncTask = new ReactContextInitAsyncTask(); mReactContextInitAsyncTask.execute(initParams); } else { // Background task is currently running, queue up most recent init params to recreate context // once task completes. mPendingReactContextInitParams = initParams; } }
public void dispatchCommand(int reactTag, int commandId, @Nullable ReadableArray args) { UiThreadUtil.assertOnUiThread(); View view = mTagsToViews.get(reactTag); if (view == null) { throw new IllegalViewOperationException( "Trying to send command to a non-existing view " + "with tag " + reactTag); } ViewManager viewManager = mTagsToViewManagers.get(reactTag); if (viewManager == null) { throw new IllegalViewOperationException( "ViewManager for view tag " + reactTag + " could not be found"); } viewManager.receiveCommand(view, commandId, args); }
@Override public void onPause() { UiThreadUtil.assertOnUiThread(); mLifecycleState = LifecycleState.BEFORE_RESUME; mDefaultBackButtonImpl = null; if (mUseDeveloperSupport) { mDevSupportManager.setDevSupportEnabled(false); } mCurrentActivity = null; if (mCurrentReactContext != null) { mCurrentReactContext.onPause(); } }
/** * Use this method when the activity resumes to enable invoking the back button directly from JS. * * <p>This method retains an instance to provided mDefaultBackButtonImpl. Thus it's important to * pass from the activity instance that owns this particular instance of {@link * ReactInstanceManagerImpl}, so that once this instance receive {@link #onDestroy} event it will * clear the reference to that defaultBackButtonImpl. * * @param defaultBackButtonImpl a {@link DefaultHardwareBackBtnHandler} from an Activity that owns * this instance of {@link ReactInstanceManagerImpl}. */ @Override public void onResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) { UiThreadUtil.assertOnUiThread(); mLifecycleState = LifecycleState.RESUMED; mDefaultBackButtonImpl = defaultBackButtonImpl; if (mUseDeveloperSupport) { mDevSupportManager.setDevSupportEnabled(true); } mCurrentActivity = activity; if (mCurrentReactContext != null) { mCurrentReactContext.onResume(activity); } }
public void updateLayout(int parentTag, int tag, int x, int y, int width, int height) { UiThreadUtil.assertOnUiThread(); View viewToUpdate = mTagsToViews.get(tag); if (viewToUpdate == null) { throw new IllegalViewOperationException( "Trying to update view with tag " + tag + " which " + "doesn't exist"); } // Even though we have exact dimensions, we still call measure because some platform views (e.g. // Switch) assume that method will always be called before onLayout and onDraw. They use it to // calculate and cache information used in the draw pass. For most views, onMeasure can be // stubbed out to only call setMeasuredDimensions. For ViewGroups, onLayout should be stubbed // out to not recursively call layout on its children: React Native already handles doing that. // // Also, note measure and layout need to be called *after* all View properties have been updated // because of caching and calculation that may occur in onMeasure and onLayout. Layout // operations should also follow the native view hierarchy and go top to bottom for consistency // with standard layout passes (some views may depend on this). viewToUpdate.measure( View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); // Check if the parent of the view has to layout the view, or the child has to lay itself out. if (!mRootTags.get(parentTag)) { ViewManager parentViewManager = mTagsToViewManagers.get(parentTag); ViewGroupManager parentViewGroupManager; if (parentViewManager instanceof ViewGroupManager) { parentViewGroupManager = (ViewGroupManager) parentViewManager; } else { throw new IllegalViewOperationException( "Trying to use view with tag " + tag + " as a parent, but its Manager doesn't extends ViewGroupManager"); } if (parentViewGroupManager != null && !parentViewGroupManager.needsCustomLayoutForChildren()) { viewToUpdate.layout(x, y, x + width, y + height); } } else { viewToUpdate.layout(x, y, x + width, y + height); } }
/** * Show a {@link PopupMenu}. * * @param reactTag the tag of the anchor view (the PopupMenu is displayed next to this view); this * needs to be the tag of a native view (shadow views can not be anchors) * @param items the menu items as an array of strings * @param success will be called with the position of the selected item as the first argument, or * no arguments if the menu is dismissed */ public void showPopupMenu(int reactTag, ReadableArray items, Callback success) { UiThreadUtil.assertOnUiThread(); View anchor = mTagsToViews.get(reactTag); if (anchor == null) { throw new JSApplicationIllegalArgumentException("Could not find view with tag " + reactTag); } PopupMenu popupMenu = new PopupMenu(getReactContextForView(reactTag), anchor); Menu menu = popupMenu.getMenu(); for (int i = 0; i < items.size(); i++) { menu.add(Menu.NONE, Menu.NONE, i, items.getString(i)); } PopupMenuCallbackHandler handler = new PopupMenuCallbackHandler(success); popupMenu.setOnMenuItemClickListener(handler); popupMenu.setOnDismissListener(handler); popupMenu.show(); }
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); } }
private void attachMeasuredRootViewToInstance( ReactRootView rootView, CatalystInstance catalystInstance) { UiThreadUtil.assertOnUiThread(); // Reset view content as it's going to be populated by the application content from JS rootView.removeAllViews(); rootView.setId(View.NO_ID); UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class); int rootTag = uiManagerModule.addMeasuredRootView(rootView); @Nullable Bundle launchOptions = rootView.getLaunchOptions(); WritableMap initialProps = launchOptions != null ? Arguments.fromBundle(launchOptions) : Arguments.createMap(); String jsAppModuleName = rootView.getJSModuleName(); WritableNativeMap appParams = new WritableNativeMap(); appParams.putDouble("rootTag", rootTag); appParams.putMap("initialProps", initialProps); catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams); }
@Override public void onDestroy() { UiThreadUtil.assertOnUiThread(); if (mReactContextInitAsyncTask != null) { mReactContextInitAsyncTask.cancel(true); } mMemoryPressureRouter.destroy(mApplicationContext); if (mUseDeveloperSupport) { mDevSupportManager.setDevSupportEnabled(false); } if (mCurrentReactContext != null) { mCurrentReactContext.onDestroy(); mCurrentReactContext = null; mHasStartedCreatingInitialContext = false; } mCurrentActivity = null; }
public void createView( int rootViewTagForContext, int tag, String className, @Nullable CatalystStylesDiffMap initialProps) { UiThreadUtil.assertOnUiThread(); ViewManager viewManager = mViewManagers.get(className); View view = viewManager.createView(mRootViewsContext.get(rootViewTagForContext), mJSResponderHandler); mTagsToViews.put(tag, view); mTagsToViewManagers.put(tag, viewManager); // Use android View id field to store React tag. This is possible since we don't inflate // React views from layout xmls. Thus it is easier to just reuse that field instead of // creating another (potentially much more expensive) mapping from view to React tag view.setId(tag); if (initialProps != null) { viewManager.updateProperties(view, initialProps); } }
/** Releases all references to given native View. */ private void dropView(View view) { UiThreadUtil.assertOnUiThread(); if (!mRootTags.get(view.getId())) { // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance} Assertions.assertNotNull(mTagsToViewManagers.get(view.getId())) .onDropViewInstance((ThemedReactContext) view.getContext(), view); } ViewManager viewManager = mTagsToViewManagers.get(view.getId()); if (view instanceof ViewGroup && viewManager instanceof ViewGroupManager) { ViewGroup viewGroup = (ViewGroup) view; ViewGroupManager viewGroupManager = (ViewGroupManager) viewManager; for (int i = viewGroupManager.getChildCount(viewGroup) - 1; i >= 0; i--) { View child = viewGroupManager.getChildAt(viewGroup, i); if (mTagsToViews.get(child.getId()) != null) { dropView(child); } } viewGroupManager.removeAllViews(viewGroup); } mTagsToViews.remove(view.getId()); mTagsToViewManagers.remove(view.getId()); }
/* package */ void startAnimationForNativeView( int reactTag, Animation animation, @Nullable final Callback animationCallback) { UiThreadUtil.assertOnUiThread(); View view = mTagsToViews.get(reactTag); final int animationId = animation.getAnimationID(); if (view != null) { animation.setAnimationListener( new AnimationListener() { @Override public void onFinished() { Animation removedAnimation = mAnimationRegistry.removeAnimation(animationId); // There's a chance that there was already a removeAnimation call enqueued on the main // thread when this callback got enqueued on the main thread, but the Animation class // should handle only calling one of onFinished and onCancel exactly once. Assertions.assertNotNull(removedAnimation, "Animation was already removed somehow!"); if (animationCallback != null) { animationCallback.invoke(true); } } @Override public void onCancel() { Animation removedAnimation = mAnimationRegistry.removeAnimation(animationId); Assertions.assertNotNull(removedAnimation, "Animation was already removed somehow!"); if (animationCallback != null) { animationCallback.invoke(false); } } }); animation.start(view); } else { // TODO(5712813): cleanup callback in JS callbacks table in case of an error throw new IllegalViewOperationException("View with tag " + reactTag + " not found"); } }
private void detachViewFromInstance(ReactRootView rootView, CatalystInstance catalystInstance) { UiThreadUtil.assertOnUiThread(); catalystInstance .getJSModule(AppRegistry.class) .unmountApplicationComponentAtRootTag(rootView.getId()); }
private void stopFrameCallback() { UiThreadUtil.assertOnUiThread(); mCurrentFrameCallback.stop(); }
@Override public void showDevOptionsDialog() { UiThreadUtil.assertOnUiThread(); mDevSupportManager.showDevOptionsDialog(); }
private void invokeDefaultOnBackPressed() { UiThreadUtil.assertOnUiThread(); if (mDefaultBackButtonImpl != null) { mDefaultBackButtonImpl.invokeDefaultOnBackPressed(); } }