Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING: Android: Refactor so uimanager can depend on modules/core #12329

Closed

Conversation

rigdern
Copy link
Contributor

@rigdern rigdern commented Feb 10, 2017

cc @astreet

The goal of this PR is to enable the buck module uimanager to depend on modules/core without introducing any dependency cycles.

PR #11008 relies on this PR. PR #11008 needs uimanager to depend on modules/core so that uimanager can fire events using RCTDeviceEventEmitter which is in modules/core.

Summary of Breaking Changes

This PR moved a number of classes and interfaces:

  • com.facebook.react.modules.debug.DeveloperSettings -> com.facebook.react.modules.debug.interfaces.DeveloperSettings
  • com.facebook.react.devsupport.DevOptionHandler -> com.facebook.react.devsupport.interfaces.DevOptionHandler
  • com.facebook.react.devsupport.DevSupportManager -> com.facebook.react.devsupport.interfaces.DevSupportManager
  • com.facebook.react.devsupport.DevServerHelper.PackagerStatusCallback -> com.facebook.react.devsupport.interfaces.PackagerStatusCallback
  • The class com.facebook.react.devsupport.StackTraceHelper.StackFrame was renamed to StackFrameImpl. StackFrameImpl implements a new interface called com.facebook.react.devsupport.interfaces.StackFrame.
  • com.facebook.react.uimanager.AppRegistry -> com.facebook.react.modules.appregistry.AppRegistry
  • com.facebook.react.uimanager.ReactChoreographer -> com.facebook.react.modules.core.ReactChoreographer

Alternative Solution

One possible solution to this problem is to move RCTDeviceEventEmitter out of modules/core into its own buck module. However, I suspect that this would be a breaking change that would affect real apps. RCTDeviceEventEmitter would have to move into a different package and I suspect RCTDeviceEventEmitter is a somewhat common API in third-party code. Consequently, I decided not to go with this approach.

This Solution

The approach implemented in this PR involves moving around a number of classes/interfaces. I believe that these class/interfaces aren't commonly used in third-party code so it won't break most apps.

I will explain the refactoring I implemented in this PR in several steps.

Step 1: Create buck module modules/debug:interfaces

The purpose of this buck module is that later we're going to create a devsupport:interfaces module. devsupport:interfaces needs to be able to depend on DeveloperSettings without depending on all of modules/debug. If it depended on all of modules/debug, then we'd have this cycle:

uimanager -> modules/core -> devsupport:interfaces -> modules/debug -> uimanager

New modules/debug:interfaces module contains:

  • DeveloperSettings

These modules depend on it:

  • devsupport
  • modules/debug
  • react

Step 2: Remove modules/core's dependency on devsupport

This change addresses the following cycle:

uimanager -> modules/core -> devsupport -> modules/debug -> uimanager

We break this cycle by removing modules/core's dependency on devsupport. We pull the devsupport stuff that module/core depends on out into its own buck module, devsupport:interfaces.

Thus, the dependency chain becomes:

uimanager -> modules/core -> devsupport:interfaces -> modules/debug:interfaces

New devsupport:interfaces module contains:

  • DevOptionHandler
  • DevSupportManager
  • PackagerStatusCallback
  • StackFrame

The PackagerStatusCallback interface was extracted from the class DevServerHelper and moved into its own file. PackagerStatusCallback is in devsupport:interfaces and DevServerHelper remains in devsupport.

StackFrame is a new interface. There used to be a class called StackFrame but it has been renamed to StackFrameImpl and it implements StackFrame. StackFrame is in devsupport:interfaces and StackFrameImpl is in devsupport.

These modules depend on the new module:

  • devsupport
  • modules/core
  • react

Step 3: Remove jstasks' dependency on uimanager

This change addresses the following cycle:

uimanager -> modules/core -> jstasks -> uimanager

We break this cycle by removing jstasks' dependency on uimanager. The only thing in uimanager that jstasks depends on is AppRegistry. We break this cycle by moving AppRegistry to its own buck module called appregistry.

Thus, the new chain becomes:

uimanager -> modules/core -> jstasks -> appregistry

New appregistry module contains:

  • AppRegistry

These modules depend on it:

  • jstasks
  • react

Step 4: Remove modules/core's dependency on uimanager

This change addresses the following cycle:

uimanager -> modules/core -> uimanager

We break this cycle by removing modules/core's dependency on uimanager. The only thing in uimanager that modules/core depends on is ReactChoreographer. We move ReactChoreographer from uimanager to modules/core.

New modules that depend on modules/core:

  • uimanager

Adam Comella
Microsoft Corp.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Feb 10, 2017
@astreet
Copy link
Contributor

astreet commented Feb 10, 2017

This looks good to me: can you title this "BREAKING: " and make it clear at the top of the description what classes people have been using that would be affected? I'll import afterwards (though I'll probably have to land manually on Monday)

Thanks!

@rigdern rigdern changed the title Android: Refactor so uimanager can depend on modules/core BREAKING: Android: Refactor so uimanager can depend on modules/core Feb 11, 2017
@rigdern
Copy link
Contributor Author

rigdern commented Feb 11, 2017

@astreet Thanks for the fast response time with the review. I updated the title and description as requested.

I have a general question related to breaking changes in Java code. This PR is breaking because I moved around some classes and interfaces. It seems to me that the public API surface that third-party modules can use is not clearly defined and third-party modules can just make use of any code that is marked public even if it wasn't intended for third-party code to consume. It seems like this makes changing RN fragile and many changes to RN's implementation details can be considered to be breaking.

Does my concern make sense or am I missing something? If my concern is legit, is there some way we could clearly define the RN API surface that is intended to be consumed by third-parties and generate a compiler error if third-party code attempts to use code that isn't exposed on that API surface?

@astreet
Copy link
Contributor

astreet commented Feb 11, 2017

@facebook-github-bot shipit

Thanks!

@astreet
Copy link
Contributor

astreet commented Feb 11, 2017

Looks like there might be some conflicts that need to be resolved

@astreet
Copy link
Contributor

astreet commented Feb 11, 2017

You're right that the surface area is not well defined: we've opted to expose a lot so people can make 3rd party modules that interact with the inner-workings of RN, but yeah, that makes them more likely to break. We do try to keep the actual JS API and standard native modules/view manager apis as stable as possible and consider changes there Big Deals (tm). Really, we probably don't need to call these breaking since the vast majority of people won't encounter problems from it, but I think it's nice to have it listed.

Adam Comella added 5 commits February 12, 2017 20:35
The purpose of this module is that later we're going to create a devsupport:interfaces module. This module needs to be able to depend on DeveloperSettings without depending on all of devsupport. If it dependen on all of devsupport, then we'd have this cycle:
  uimanager -> modules/core -> devsupport:interfaces -> modules/debug -> uimanager

New module contains:
  - DeveloperSettings

These modules depend on it:
  - modules/debug
  - devsupport
  - react
This change addresses the following cycle:
  uimanager -> modules/core -> devsupport -> modules/debug -> uimanager

We break this cycle by removing modules/core's dependency on devsupport. Instead, it depends on devsupport:interfaces.

Thus, the dependency chain becomes:
  -   uimanager -> modules/core -> devsupport:interfaces -> modules/debug:interfaces

New module contains:
  - DevOptionHandler
  - DevSupportManager
  - PackagerStatusCallback
  - StackFrame

The PackagerStatusCallback interface was extracted from the class DevServerHelper and moved into its own file. PackagerStatusCallback is in devsupport:interfaces and DevServerHelper remains in devsupport.

StackFrame is a new interface. There used to be a class called StackFrame but it has been renamed to StackFrameImpl and it implements StackFrame. StackFrame is in devsupport:interfaces and StackFrameImpl is in devsupport.

These modules depend on the new module:
  - devsupport
  - modules/core
  - react
This change addresses the following cycle:
      uimanager -> modules/core -> jstasks -> uimanager

We break this cycle by removing jstasks' dependency on uimanager. The only thing in uimanager that jstasks depends on is AppRegistry. We break this cycle by moving AppRegistry to its own buck module called appregistry.

Thus, the new chain becomes:
  uimanager -> modules/core -> jstasks -> appregistry

New module contains:
  - AppRegistry

These modules depend on it:
  - jstasks
  - react
This change addresses the following cycle:
  uimanager -> modules/core -> uimanager

We break this cycle by removing modules/core's dependency on uimanager. The only thing in uimanager that modules/core depends on in ReactChoreographer. We move ReactChoreographer from uimanager to modules/core.

New modules that depend on modules/core:
  - uimanager
@rigdern rigdern force-pushed the rigdern/breakDependencyCycles branch from 1f3503f to eb787dd Compare February 13, 2017 04:36
@rigdern
Copy link
Contributor Author

rigdern commented Feb 13, 2017

@astreet I rebased and resolved the merge conflicts.

@astreet
Copy link
Contributor

astreet commented Feb 13, 2017

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added GH Review: accepted Import Started This pull request has been imported. This does not imply the PR has been approved. and removed GH Review: review-needed labels Feb 13, 2017
@facebook-github-bot
Copy link
Contributor

@astreet has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@rigdern rigdern deleted the rigdern/breakDependencyCycles branch February 13, 2017 20:25
GaborWnuk pushed a commit to GaborWnuk/react-native that referenced this pull request Feb 28, 2017
Summary:
cc astreet

The goal of this PR is to enable the buck module `uimanager` to depend on `modules/core` without introducing any dependency cycles.

PR facebook#11008 relies on this PR. PR facebook#11008 needs `uimanager` to depend on `modules/core` so that `uimanager` can fire events using `RCTDeviceEventEmitter` which is in `modules/core`.

This PR moved a number of classes and interfaces:
  - `com.facebook.react.modules.debug.DeveloperSettings` -> `com.facebook.react.modules.debug.interfaces.DeveloperSettings`
  - `com.facebook.react.devsupport.DevOptionHandler` -> `com.facebook.react.devsupport.interfaces.DevOptionHandler `
  - `com.facebook.react.devsupport.DevSupportManager` -> `com.facebook.react.devsupport.interfaces.DevSupportManager`
  - `com.facebook.react.devsupport.DevServerHelper.PackagerStatusCallback` -> `com.facebook.react.devsupport.interfaces.PackagerStatusCallback`
  - The class `com.facebook.react.devsupport.StackTraceHelper.StackFrame` was renamed to `StackFram
Closes facebook#12329

Differential Revision: D4551160

Pulled By: astreet

fbshipit-source-id: 3a78443c4f30469b13ddfbdcc9bbef6af9e8381a
dudeinthemirror pushed a commit to dudeinthemirror/react-native that referenced this pull request Mar 1, 2017
Summary:
cc astreet

The goal of this PR is to enable the buck module `uimanager` to depend on `modules/core` without introducing any dependency cycles.

PR facebook#11008 relies on this PR. PR facebook#11008 needs `uimanager` to depend on `modules/core` so that `uimanager` can fire events using `RCTDeviceEventEmitter` which is in `modules/core`.

This PR moved a number of classes and interfaces:
  - `com.facebook.react.modules.debug.DeveloperSettings` -> `com.facebook.react.modules.debug.interfaces.DeveloperSettings`
  - `com.facebook.react.devsupport.DevOptionHandler` -> `com.facebook.react.devsupport.interfaces.DevOptionHandler `
  - `com.facebook.react.devsupport.DevSupportManager` -> `com.facebook.react.devsupport.interfaces.DevSupportManager`
  - `com.facebook.react.devsupport.DevServerHelper.PackagerStatusCallback` -> `com.facebook.react.devsupport.interfaces.PackagerStatusCallback`
  - The class `com.facebook.react.devsupport.StackTraceHelper.StackFrame` was renamed to `StackFram
Closes facebook#12329

Differential Revision: D4551160

Pulled By: astreet

fbshipit-source-id: 3a78443c4f30469b13ddfbdcc9bbef6af9e8381a
dudeinthemirror pushed a commit to dudeinthemirror/react-native that referenced this pull request Mar 1, 2017
Summary:
cc astreet

The goal of this PR is to enable the buck module `uimanager` to depend on `modules/core` without introducing any dependency cycles.

PR facebook#11008 relies on this PR. PR facebook#11008 needs `uimanager` to depend on `modules/core` so that `uimanager` can fire events using `RCTDeviceEventEmitter` which is in `modules/core`.

This PR moved a number of classes and interfaces:
  - `com.facebook.react.modules.debug.DeveloperSettings` -> `com.facebook.react.modules.debug.interfaces.DeveloperSettings`
  - `com.facebook.react.devsupport.DevOptionHandler` -> `com.facebook.react.devsupport.interfaces.DevOptionHandler `
  - `com.facebook.react.devsupport.DevSupportManager` -> `com.facebook.react.devsupport.interfaces.DevSupportManager`
  - `com.facebook.react.devsupport.DevServerHelper.PackagerStatusCallback` -> `com.facebook.react.devsupport.interfaces.PackagerStatusCallback`
  - The class `com.facebook.react.devsupport.StackTraceHelper.StackFrame` was renamed to `StackFram
Closes facebook#12329

Differential Revision: D4551160

Pulled By: astreet

fbshipit-source-id: 3a78443c4f30469b13ddfbdcc9bbef6af9e8381a
sergey-akhalkov pushed a commit to microsoft/react-native-code-push that referenced this pull request Mar 23, 2017
Fixed android project build issues that have place due to the following breaking changes: 
facebook/react-native#12329
facebook/react-native#12396
maarf pushed a commit to fullcontact/react-native that referenced this pull request Apr 26, 2017
Summary:
cc astreet

The goal of this PR is to enable the buck module `uimanager` to depend on `modules/core` without introducing any dependency cycles.

PR facebook#11008 relies on this PR. PR facebook#11008 needs `uimanager` to depend on `modules/core` so that `uimanager` can fire events using `RCTDeviceEventEmitter` which is in `modules/core`.

This PR moved a number of classes and interfaces:
  - `com.facebook.react.modules.debug.DeveloperSettings` -> `com.facebook.react.modules.debug.interfaces.DeveloperSettings`
  - `com.facebook.react.devsupport.DevOptionHandler` -> `com.facebook.react.devsupport.interfaces.DevOptionHandler `
  - `com.facebook.react.devsupport.DevSupportManager` -> `com.facebook.react.devsupport.interfaces.DevSupportManager`
  - `com.facebook.react.devsupport.DevServerHelper.PackagerStatusCallback` -> `com.facebook.react.devsupport.interfaces.PackagerStatusCallback`
  - The class `com.facebook.react.devsupport.StackTraceHelper.StackFrame` was renamed to `StackFram
Closes facebook#12329

Differential Revision: D4551160

Pulled By: astreet

fbshipit-source-id: 3a78443c4f30469b13ddfbdcc9bbef6af9e8381a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Import Started This pull request has been imported. This does not imply the PR has been approved.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants