├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── build-android.yml │ ├── build-ios.yml │ ├── validate-android.yml │ ├── validate-cpp.yml │ └── validate-js.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SETUP.md ├── android ├── .project ├── .settings │ └── org.eclipse.buildship.core.prefs ├── CMakeLists.txt ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── AndroidErrorHandler.h │ ├── AndroidScheduler.h │ └── cpp-adapter.cpp │ └── java │ └── com │ └── reactnativemultithreading │ ├── MultithreadingJSIModulePackage.java │ ├── MultithreadingModule.java │ └── MultithreadingPackage.java ├── babel.config.js ├── cpp ├── MakeJSIRuntime.cpp ├── MakeJSIRuntime.h ├── RNMultithreadingInstaller.cpp ├── RNMultithreadingInstaller.h ├── ThreadPool.cpp └── ThreadPool.h ├── example ├── android │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── reactnativemultithreading │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── reactnativemultithreading │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── globals.d.ts ├── index.js ├── ios │ ├── File.swift │ ├── Gemfile │ ├── Gemfile.lock │ ├── MultithreadingExample-Bridging-Header.h │ ├── MultithreadingExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── MultithreadingExample.xcscheme │ ├── MultithreadingExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── MultithreadingExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── main.m │ ├── Podfile │ └── Podfile.lock ├── metro.config.js ├── package.json ├── src │ ├── Fibonacci.tsx │ ├── PressableOpacity.tsx │ └── ThreadBlocking.tsx └── yarn.lock ├── img └── icon.png ├── ios ├── Multithreading.xcodeproj │ └── project.pbxproj ├── RNMultithreading.h └── RNMultithreading.mm ├── package.json ├── react-native-multithreading.podspec ├── scripts ├── build-android.sh └── template.build.gradle ├── src ├── __tests__ │ └── index.test.tsx ├── globals.d.ts └── index.ts ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: mrousavy 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: mrousavy 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/build-android.yml: -------------------------------------------------------------------------------- 1 | name: Build Android App 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/workflows/build-android.yml' 9 | - 'android/**' 10 | - 'example/android/**' 11 | - 'yarn.lock' 12 | - 'example/yarn.lock' 13 | pull_request: 14 | paths: 15 | - '.github/workflows/build-android.yml' 16 | - 'android/**' 17 | - 'example/android/**' 18 | - 'yarn.lock' 19 | - 'example/yarn.lock' 20 | 21 | jobs: 22 | build: 23 | name: Build Android Example App 24 | runs-on: ubuntu-latest 25 | defaults: 26 | run: 27 | working-directory: example/android 28 | steps: 29 | - uses: actions/checkout@v2 30 | 31 | - name: Setup JDK 1.8 32 | uses: actions/setup-java@v1 33 | with: 34 | java-version: 1.8 35 | 36 | - name: Get yarn cache directory path 37 | id: yarn-cache-dir-path 38 | run: echo "::set-output name=dir::$(yarn cache dir)" 39 | - name: Restore node_modules from cache 40 | uses: actions/cache@v2 41 | id: yarn-cache 42 | with: 43 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 44 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 45 | restore-keys: | 46 | ${{ runner.os }}-yarn- 47 | - name: Install node_modules for example/ 48 | run: yarn install --frozen-lockfile --cwd .. 49 | 50 | - name: Restore Gradle cache 51 | uses: actions/cache@v2 52 | with: 53 | path: | 54 | ~/.gradle/caches 55 | ~/.gradle/wrapper 56 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 57 | restore-keys: | 58 | ${{ runner.os }}-gradle- 59 | - name: Run Gradle Build 60 | run: ./gradlew assembleDebug 61 | -------------------------------------------------------------------------------- /.github/workflows/build-ios.yml: -------------------------------------------------------------------------------- 1 | name: Build iOS App 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/workflows/build-ios.yml' 9 | - 'ios/**' 10 | - '*.podspec' 11 | - 'example/ios/**' 12 | pull_request: 13 | paths: 14 | - '.github/workflows/build-ios.yml' 15 | - 'ios/**' 16 | - '*.podspec' 17 | - 'example/ios/**' 18 | 19 | jobs: 20 | build: 21 | name: Build iOS Example App 22 | runs-on: macOS-latest 23 | defaults: 24 | run: 25 | working-directory: example/ios 26 | steps: 27 | - uses: actions/checkout@v2 28 | 29 | - name: Get yarn cache directory path 30 | id: yarn-cache-dir-path 31 | run: echo "::set-output name=dir::$(yarn cache dir)" 32 | - name: Restore node_modules from cache 33 | uses: actions/cache@v2 34 | id: yarn-cache 35 | with: 36 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 37 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 38 | restore-keys: | 39 | ${{ runner.os }}-yarn- 40 | - name: Install node_modules for example/ 41 | run: yarn install --frozen-lockfile --cwd .. 42 | 43 | - name: Setup Ruby (bundle) 44 | uses: ruby/setup-ruby@v1 45 | with: 46 | ruby-version: 2.6 47 | bundler-cache: true 48 | working-directory: example/ios 49 | 50 | - name: Restore Pods cache 51 | uses: actions/cache@v2 52 | with: 53 | path: | 54 | example/ios/Pods 55 | ~/Library/Caches/CocoaPods 56 | ~/.cocoapods 57 | key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} 58 | restore-keys: | 59 | ${{ runner.os }}-pods- 60 | - name: Install Pods 61 | run: bundle exec pod check || bundle exec pod install 62 | - name: Build App 63 | run: "xcodebuild \ 64 | -workspace MultithreadingExample.xcworkspace \ 65 | -scheme MultithreadingExample \ 66 | -sdk iphonesimulator \ 67 | -configuration Debug \ 68 | -destination \"generic/platform=iOS Simulator\" \ 69 | build \ 70 | CODE_SIGNING_ALLOWED=NO" 71 | -------------------------------------------------------------------------------- /.github/workflows/validate-android.yml: -------------------------------------------------------------------------------- 1 | name: Validate Android 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/workflows/validate-android.yml' 9 | - 'android/**' 10 | - '.editorconfig' 11 | pull_request: 12 | paths: 13 | - '.github/workflows/validate-android.yml' 14 | - 'android/**' 15 | - '.editorconfig' 16 | 17 | jobs: 18 | lint: 19 | name: Gradle Lint 20 | runs-on: ubuntu-latest 21 | defaults: 22 | run: 23 | working-directory: ./android 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup JDK 1.8 27 | uses: actions/setup-java@v1 28 | with: 29 | java-version: 1.8 30 | 31 | - name: Get yarn cache directory path 32 | id: yarn-cache-dir-path 33 | run: echo "::set-output name=dir::$(yarn cache dir)" 34 | - name: Restore node_modules from cache 35 | uses: actions/cache@v2 36 | id: yarn-cache 37 | with: 38 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 39 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-yarn- 42 | - name: Install node_modules 43 | run: yarn install --frozen-lockfile --cwd .. 44 | 45 | - name: Restore Gradle cache 46 | uses: actions/cache@v2 47 | with: 48 | path: | 49 | ~/.gradle/caches 50 | ~/.gradle/wrapper 51 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 52 | restore-keys: | 53 | ${{ runner.os }}-gradle- 54 | - name: Run Gradle Lint 55 | run: ./gradlew lint 56 | 57 | - name: Parse Gradle Lint Report 58 | uses: yutailang0119/action-android-lint@v1.0.2 59 | with: 60 | xml_path: android/build/reports/lint-results.xml 61 | -------------------------------------------------------------------------------- /.github/workflows/validate-cpp.yml: -------------------------------------------------------------------------------- 1 | name: Validate C++ 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/workflows/validate-cpp.yml' 9 | - 'cpp/**' 10 | pull_request: 11 | paths: 12 | - '.github/workflows/validate-cpp.yml' 13 | - 'cpp/**' 14 | 15 | jobs: 16 | lint: 17 | name: cpplint 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: reviewdog/action-cpplint@master 22 | with: 23 | github_token: ${{ secrets.github_token }} 24 | reporter: github-pr-review 25 | flags: --linelength=230 26 | targets: --recursive cpp android/src/main/cpp 27 | filter: "-legal/copyright\ 28 | ,-readability/todo\ 29 | ,-build/namespaces\ 30 | ,-whitespace/comments\ 31 | " 32 | -------------------------------------------------------------------------------- /.github/workflows/validate-js.yml: -------------------------------------------------------------------------------- 1 | name: Validate JS 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/workflows/validate-js.yml' 9 | - 'src/**' 10 | - '*.json' 11 | - '*.js' 12 | - '*.lock' 13 | - 'example/src/**' 14 | - 'example/*.json' 15 | - 'example/*.js' 16 | - 'example/*.lock' 17 | - 'example/*.tsx' 18 | pull_request: 19 | paths: 20 | - '.github/workflows/validate-js.yml' 21 | - 'src/**' 22 | - '*.json' 23 | - '*.js' 24 | - '*.lock' 25 | - 'example/src/**' 26 | - 'example/*.json' 27 | - 'example/*.js' 28 | - 'example/*.lock' 29 | - 'example/*.tsx' 30 | 31 | jobs: 32 | compile: 33 | name: Compile JS (tsc) 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v2 37 | 38 | - name: Install reviewdog 39 | uses: reviewdog/action-setup@v1 40 | 41 | - name: Get yarn cache directory path 42 | id: yarn-cache-dir-path 43 | run: echo "::set-output name=dir::$(yarn cache dir)" 44 | - name: Restore node_modules from cache 45 | uses: actions/cache@v2 46 | id: yarn-cache 47 | with: 48 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 49 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 50 | restore-keys: | 51 | ${{ runner.os }}-yarn- 52 | - name: Install node_modules 53 | run: yarn install --frozen-lockfile 54 | - name: Install node_modules (example/) 55 | run: yarn install --frozen-lockfile --cwd example 56 | 57 | - name: Run TypeScript # Reviewdog tsc errorformat: %f:%l:%c - error TS%n: %m 58 | run: | 59 | yarn typescript | reviewdog -name="tsc" -efm="%f(%l,%c): error TS%n: %m" -reporter="github-pr-review" -filter-mode="nofilter" -fail-on-error -tee 60 | env: 61 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 62 | 63 | lint: 64 | name: Lint JS (eslint, prettier) 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v2 68 | 69 | - name: Get yarn cache directory path 70 | id: yarn-cache-dir-path 71 | run: echo "::set-output name=dir::$(yarn cache dir)" 72 | - name: Restore node_modules from cache 73 | uses: actions/cache@v2 74 | id: yarn-cache 75 | with: 76 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 77 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 78 | restore-keys: | 79 | ${{ runner.os }}-yarn- 80 | - name: Install node_modules 81 | run: yarn install --frozen-lockfile 82 | - name: Install node_modules (example/) 83 | run: yarn install --frozen-lockfile --cwd example 84 | 85 | - name: Run ESLint 86 | run: yarn lint -f @jamesacarr/github-actions 87 | 88 | - name: Run ESLint with auto-fix 89 | run: yarn lint --fix 90 | 91 | - name: Verify no files have changed after auto-fix 92 | run: git diff --exit-code HEAD 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .idea 35 | .gradle 36 | local.properties 37 | android.iml 38 | 39 | # Cocoapods 40 | # 41 | example/ios/Pods 42 | 43 | # node.js 44 | # 45 | node_modules/ 46 | npm-debug.log 47 | yarn-debug.log 48 | yarn-error.log 49 | 50 | # BUCK 51 | buck-out/ 52 | \.buckd/ 53 | android/app/libs 54 | android/keystores/debug.keystore 55 | 56 | # Expo 57 | .expo/* 58 | 59 | # generated by bob 60 | lib/ 61 | 62 | .cxx/ 63 | .externalNativeBuild/ 64 | native_prebuilt 65 | ndk-deps 66 | 67 | android-npm 68 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. 4 | 5 | ## Development workflow 6 | 7 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 8 | 9 | ```sh 10 | yarn 11 | ``` 12 | 13 | While developing, you can run the [example app](/example/) to test your changes. 14 | 15 | To start the packager: 16 | 17 | ```sh 18 | yarn example start 19 | ``` 20 | 21 | To run the example app on Android: 22 | 23 | ```sh 24 | yarn example android 25 | ``` 26 | 27 | To run the example app on iOS: 28 | 29 | ```sh 30 | yarn example ios 31 | ``` 32 | 33 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 34 | 35 | ```sh 36 | yarn typescript 37 | yarn lint 38 | ``` 39 | 40 | To fix formatting errors, run the following: 41 | 42 | ```sh 43 | yarn lint --fix 44 | ``` 45 | 46 | Remember to add tests for your change if possible. Run the unit tests by: 47 | 48 | ```sh 49 | yarn test 50 | ``` 51 | 52 | To edit the Objective-C files, open `example/ios/MultithreadingExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-multithreading`. 53 | 54 | To edit the Java files, open `example/android` in Android studio and find the source files at `reactnativemultithreading` under `Android`. 55 | 56 | ### Commit message convention 57 | 58 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 59 | 60 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 61 | - `feat`: new features, e.g. add new method to the module. 62 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 63 | - `docs`: changes into documentation, e.g. add usage example for the module.. 64 | - `test`: adding or updating tests, e.g. add integration tests using detox. 65 | - `chore`: tooling changes, e.g. change CI config. 66 | 67 | Our pre-commit hooks verify that your commit message matches this format when committing. 68 | 69 | ### Linting and tests 70 | 71 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 72 | 73 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 74 | 75 | Our pre-commit hooks verify that the linter and tests pass when committing. 76 | 77 | ### Scripts 78 | 79 | The `package.json` file contains various scripts for common tasks: 80 | 81 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 82 | - `yarn typescript`: type-check files with TypeScript. 83 | - `yarn lint`: lint files with ESLint. 84 | - `yarn test`: run unit tests with Jest. 85 | - `yarn example start`: start the Metro server for the example app. 86 | - `yarn example android`: run the example app on Android. 87 | - `yarn example ios`: run the example app on iOS. 88 | 89 | ### Sending a pull request 90 | 91 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). 92 | 93 | When you're sending a pull request: 94 | 95 | - Prefer small pull requests focused on one change. 96 | - Verify that linters and tests are passing. 97 | - Review the documentation to make sure it looks good. 98 | - Follow the pull request template when opening a pull request. 99 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 100 | 101 | ## Code of Conduct 102 | 103 | ### Our Pledge 104 | 105 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 106 | 107 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 108 | 109 | ### Our Standards 110 | 111 | Examples of behavior that contributes to a positive environment for our community include: 112 | 113 | - Demonstrating empathy and kindness toward other people 114 | - Being respectful of differing opinions, viewpoints, and experiences 115 | - Giving and gracefully accepting constructive feedback 116 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 117 | - Focusing on what is best not just for us as individuals, but for the overall community 118 | 119 | Examples of unacceptable behavior include: 120 | 121 | - The use of sexualized language or imagery, and sexual attention or 122 | advances of any kind 123 | - Trolling, insulting or derogatory comments, and personal or political attacks 124 | - Public or private harassment 125 | - Publishing others' private information, such as a physical or email 126 | address, without their explicit permission 127 | - Other conduct which could reasonably be considered inappropriate in a 128 | professional setting 129 | 130 | ### Enforcement Responsibilities 131 | 132 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 133 | 134 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 135 | 136 | ### Scope 137 | 138 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 139 | 140 | ### Enforcement 141 | 142 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly. 143 | 144 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 145 | 146 | ### Enforcement Guidelines 147 | 148 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 149 | 150 | #### 1. Correction 151 | 152 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 153 | 154 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 155 | 156 | #### 2. Warning 157 | 158 | **Community Impact**: A violation through a single incident or series of actions. 159 | 160 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 161 | 162 | #### 3. Temporary Ban 163 | 164 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 165 | 166 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 167 | 168 | #### 4. Permanent Ban 169 | 170 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 171 | 172 | **Consequence**: A permanent ban from any sort of public interaction within the community. 173 | 174 | ### Attribution 175 | 176 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, 177 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 178 | 179 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 180 | 181 | [homepage]: https://www.contributor-covenant.org 182 | 183 | For answers to common questions about this code of conduct, see the FAQ at 184 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 185 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Marc Rousavy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

react-native-multithreading

4 |

🧵 Fast and easy multithreading for React Native using JSI. 🧵

5 |

6 | ⚠️ Note: This is a proof of concept.
7 | Do not use this library in production.

8 | VisionCamera goes beyond this concept and
9 | use a separate JS runtime to provide an API for processing
10 | camera frames in realtime. Check out Frame Processors! 11 |

12 |
13 | 14 | Buy Me a Coffee at ko-fi.com 15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | ## Installation 28 | 29 | ```sh 30 | npm install react-native-multithreading 31 | npx pod-install 32 | ``` 33 | 34 | Since JSI is not officially released, installing **react-native-multithreading** requires you to edit a few native files. See [the setup guide (**SETUP.md**)](./SETUP.md) for more details. 35 | 36 | > Requires react-native-reanimated [**2.1.0**](https://github.com/software-mansion/react-native-reanimated/releases/tag/2.1.0) or higher. 37 | 38 | > 🎉 [v1.0](https://github.com/mrousavy/react-native-multithreading/releases/tag/1.0.0) with Android support is here! 🎉 39 | 40 | ## Why 41 | 42 | Since [JSI](https://github.com/react-native-community/discussions-and-proposals/issues/91) is becoming more mainstream, there might be functions that are actually blocking and take a while to execute. For example, a storage library like [my react-native-mmkv](https://github.com/mrousavy/react-native-mmkv) or an SQLite JSI library might take a few milliseconds to execute a complex call. You don't want your entire React-JS thread to freeze when doing that, since users will perceive a noticeable lag or freeze. 43 | 44 | That's where **react-native-multithreading** comes in; you can simply off-load such expensive calculations/blocking calls to a separate thread with almost no overhead while your main React-JS thread can concentrate on running your app's business logic, respond to user input, update state and more. You can also run complex JS calculations such as the [Fibonacci number](https://en.wikipedia.org/wiki/Fibonacci_number), but that's probably a rare use-case. 45 | 46 | > Inspired by [**@karol-bisztyga**'s Multithreading PR for Reanimated](https://github.com/software-mansion/react-native-reanimated/pull/1561) 47 | 48 | ## Usage 49 | 50 | To try out the Fibonacci Example, clone the repo and run the following commands: 51 | 52 | ``` 53 | yarn bootstrap 54 | cd example 55 | yarn ios 56 | ``` 57 | 58 | > See [my tweet 🐦](https://twitter.com/mrousavy/status/1371793888273432577) 59 | 60 | ### Shoot and Forget 61 | 62 | To simply perform an expensive calculation on another thread without caring about the result, use the `spawnThread` function: 63 | 64 | ```ts 65 | // JS thread 66 | spawnThread(() => { 67 | 'worklet' 68 | // custom thread 69 | // expensive calculation 70 | }) 71 | // JS thread 72 | ``` 73 | 74 | The React-JS Thread will continue execution while the custom thread will run the given function on a custom parallel runtime. 75 | 76 | ### Await 77 | 78 | Since `spawnThread` returns a `Promise`, you can also await the result. The React-JS Thread will not be blocked and will still be able to continue execution elsewhere (timers, callbacks, rendering, ...), while the custom thread runs the given function in a custom parallel runtime. 79 | 80 | ```ts 81 | const result = await spawnThread(() => { 82 | 'worklet' 83 | // expensive calculation 84 | return ... 85 | }) 86 | ``` 87 | 88 | ### Fibonacci 89 | 90 | This example calculates the [Fibonacci Number](https://en.wikipedia.org/wiki/Fibonacci_number) for the given input. This demonstrates expensive calculation, awaiting the result, as well as using values from "outside". (`fibonacci` function and `input` are captured into the new thread and therefore immutable.) 91 | 92 | ```ts 93 | const fibonacci = (num: number): number => { 94 | 'worklet' 95 | if (num <= 1) return 1 96 | return fibonacci(num - 1) + fibonacci(num - 2) 97 | } 98 | 99 | const input = 50 100 | const result = await spawnThread(() => { 101 | 'worklet' 102 | console.log(`calculating fibonacci for input: ${input} in JS-Runtime: ${global._LABEL}...`) 103 | const fib = fibonacci(input) 104 | console.log("finished calculating fibonacci!") 105 | return fib 106 | }) 107 | console.log(`Fibonacci Result: ${result}`) 108 | ``` 109 | 110 | ## What's possible? 111 | 112 | * You can run any JavaScript code you want in there. 113 | * You can use variables from "outside" (e.g. state), but those will be immutable/frozen. 114 | * You can use functions from "outside". 115 | - Worklets (functions with the `'worklet'` directive) can be called directly on the separate thread 116 | - Native JSI functions ("host functions", aka functions that print `function f() { [native code] }` when you call `.toString()` on them) can be called synchronously (e.g. functions from [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv#usage)) 117 | - Normal JS functions (e.g. setState) can be called on the React-JS thread with [`runOnJS`](https://docs.swmansion.com/react-native-reanimated/docs/api/runOnJS) 118 | * You can assign Reanimated Shared Values. 119 | 120 | > Note that react-native-multithreading is more of a proof of concept than a production ready library. Everything works as I listed it here, but in real world app you most likely won't be needing a JS multithreading library. 121 | 122 | ## What's not possible? 123 | 124 | 1. Since the library uses JSI for synchronous native methods access, remote debugging (e.g. with Chrome) is no longer possible. Instead, you should use [Flipper](https://fbflipper.com). 125 | 2. All functions you are calling inside a custom thread, must be workletized to truly run on a separate thread. So add the `'worklet'` directive at the top of every function you're calling in that thread (including the thread callback itself), and don't forget to install the Reanimated babel plugin. 126 | 127 | ## Supported JS engines 128 | 129 | * JavaScript Core (JSC) 130 | * [Hermes](http://hermesengine.dev) 131 | * [V8](http://github.com/Kudo/react-native-v8) (iOS only) 132 | 133 | ## Performance 134 | 135 | Since the worklets are completely dispatched in an isolated thread, nothing interrupts their execution. 136 | 137 | Be aware that there always will be a small overhead when calling `spawnThread`, because all variables from outside have to be copied into the new thread first. For example, if you use the separate thread to do complex array operations, be aware that the array has to be copied into the separate thread first. Always benchmark the performance differences! 138 | 139 | ## Credits 140 | 141 | * [react-native-reanimated](http://github.com/software-mansion/react-native-reanimated) for Shared Value adapting, essentially allowing JSI multithreading 142 | * [**@karol-bisztyga**](https://github.com/karol-bisztyga) and [**@piaskowyk**](https://github.com/piaskowyk) for helping me understand a few parts of Reanimated better 143 | * [**@Szymon20000**](https://github.com/Szymon20000) for taking time to review and merge my Reanimated PRs 144 | * [JNI tips](https://developer.android.com/training/articles/perf-jni) and [fbjni](https://github.com/facebookincubator/fbjni) to make Android JNI interop easier 145 | * [**@iamyellow**](https://github.com/iamyellow) for being a huge help on the Android side 146 | * [Erik the Coder](https://www.erikthecoder.net/2019/03/30/async-does-not-imply-concurrent/) for the Icon 147 | * You, for appreciating my work 148 | 149 | > Note: Technically this is not [multithreading](https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)), but rather [multiprocessing](https://en.wikipedia.org/wiki/Multiprocessing). 150 | -------------------------------------------------------------------------------- /SETUP.md: -------------------------------------------------------------------------------- 1 | # Setup Guide 2 | 3 | ## iOS 4 | 5 | Run: 6 | 7 | ```sh 8 | cd ios 9 | pod install 10 | ``` 11 | 12 | ## Android 13 | 14 | Since pure JSI Modules cannot be autolinked yet, you have to manually initialize them. 15 | 16 |
17 | 18 | Without react-native-mmkv (or other JSI libs) 19 | 20 |
21 | 22 | 1. Open your app's `MainApplication.java` 23 | 2. Add the following code: 24 | ```diff 25 | package com.example.reactnativemultithreading; 26 | 27 | import android.app.Application; 28 | import android.content.Context; 29 | import com.facebook.react.PackageList; 30 | import com.facebook.react.ReactApplication; 31 | import com.facebook.react.ReactNativeHost; 32 | import com.facebook.react.ReactPackage; 33 | import com.facebook.react.ReactInstanceManager; 34 | import com.facebook.soloader.SoLoader; 35 | import java.lang.reflect.InvocationTargetException; 36 | import java.util.List; 37 | 38 | +import com.reactnativemultithreading.MultithreadingJSIModulePackage; 39 | +import com.facebook.react.bridge.JSIModulePackage; 40 | 41 | public class MainApplication extends Application implements ReactApplication { 42 | 43 | private final ReactNativeHost mReactNativeHost = 44 | new ReactNativeHost(this) { 45 | @Override 46 | public boolean getUseDeveloperSupport() { 47 | return BuildConfig.DEBUG; 48 | } 49 | 50 | @Override 51 | protected List getPackages() { 52 | @SuppressWarnings("UnnecessaryLocalVariable") 53 | List packages = new PackageList(this).getPackages(); 54 | // Packages that cannot be autolinked yet can be added manually here, for MultithreadingExample: 55 | // packages.add(new MyReactNativePackage()); 56 | packages.add(new MultithreadingPackage()); 57 | return packages; 58 | } 59 | 60 | @Override 61 | protected String getJSMainModuleName() { 62 | return "index"; 63 | } 64 | 65 | + // TODO: Remove this when JSI Modules can be autoinstalled (maybe RN 0.65) 66 | + @Override 67 | + protected JSIModulePackage getJSIModulePackage() { 68 | + return new MultithreadingJSIModulePackage(); 69 | + } 70 | }; 71 | ``` 72 | 73 |
74 | 75 | 76 | 77 |
78 | 79 | With react-native-mmkv (or other JSI libs) 80 | 81 |
82 | 83 | 1. Open your project in Android Studio 84 | 2. Open the folder where `MainApplication.java` lives (`src/main/java/...`) 85 | 3. Right click the folder, **New** > **Java class** 86 | 4. Call it whatever you prefer, in my case it's `ExampleJSIPackage` because my app is called "Example" 87 | 5. Add the following code: 88 | 89 | ```java 90 | package com.example; 91 | 92 | import com.facebook.react.bridge.JSIModuleSpec; 93 | import com.facebook.react.bridge.JavaScriptContextHolder; 94 | import com.facebook.react.bridge.ReactApplicationContext; 95 | import com.swmansion.reanimated.ReanimatedJSIModulePackage; 96 | import com.reactnativemmkv.MultithreadingModule; 97 | 98 | import java.util.Collections; 99 | import java.util.List; 100 | 101 | // TODO: Remove all of this when JSI Modules can be autoinstalled (maybe RN 0.65) 102 | public class ExampleJSIPackage extends ReanimatedJSIModulePackage { 103 | @Override 104 | public List getJSIModules(ReactApplicationContext reactApplicationContext, JavaScriptContextHolder jsContext) { 105 | super.getJSIModules(reactApplicationContext, jsContext); 106 | MultithreadingModule.install(reactApplicationContext, jsContext); 107 | return Collections.emptyList(); 108 | } 109 | } 110 | ``` 111 | 6. Replace `com.example` (first line) with the correct package name 112 | 7. Replace `ExampleJSIPackage` with the file name you chose in step 4. 113 | 8. Open `MainApplication.java` and find the location where the `ReactNativeHost` is initialized. You have to override it's `getJSIModulePackage` method: 114 | ```diff 115 | package com.example.reactnativemultithreading; 116 | 117 | import android.app.Application; 118 | import android.content.Context; 119 | import com.facebook.react.PackageList; 120 | import com.facebook.react.ReactApplication; 121 | import com.facebook.react.ReactNativeHost; 122 | import com.facebook.react.ReactPackage; 123 | import com.facebook.react.ReactInstanceManager; 124 | import com.facebook.soloader.SoLoader; 125 | import java.lang.reflect.InvocationTargetException; 126 | import java.util.List; 127 | 128 | +import com.example.ExampleJSIPackage; 129 | +import com.facebook.react.bridge.JSIModulePackage; 130 | 131 | public class MainApplication extends Application implements ReactApplication { 132 | 133 | private final ReactNativeHost mReactNativeHost = 134 | new ReactNativeHost(this) { 135 | @Override 136 | public boolean getUseDeveloperSupport() { 137 | return BuildConfig.DEBUG; 138 | } 139 | 140 | @Override 141 | protected List getPackages() { 142 | @SuppressWarnings("UnnecessaryLocalVariable") 143 | List packages = new PackageList(this).getPackages(); 144 | // Packages that cannot be autolinked yet can be added manually here, for MultithreadingExample: 145 | // packages.add(new MyReactNativePackage()); 146 | packages.add(new MultithreadingPackage()); 147 | return packages; 148 | } 149 | 150 | @Override 151 | protected String getJSMainModuleName() { 152 | return "index"; 153 | } 154 | 155 | + // TODO: Remove this when JSI Modules can be autoinstalled (maybe RN 0.65) 156 | + @Override 157 | + protected JSIModulePackage getJSIModulePackage() { 158 | + return new ExampleJSIModulePackage(); 159 | + } 160 | }; 161 | ``` 162 | 163 |
164 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android_ 4 | Project android_ created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.0)) 5 | connection.project.dir= 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | set (CMAKE_VERBOSE_MAKEFILE ON) 4 | set (CMAKE_CXX_STANDARD 14) 5 | set (CMAKE_CXX_FLAGS "-DFOLLY_NO_CONFIG=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_HAVE_MEMRCHR=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_MOBILE=1 -DON_ANDROID") 6 | 7 | set (PACKAGE_NAME "rnmultithreading") 8 | set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build) 9 | 10 | # rnmultithreading shared 11 | 12 | add_library( 13 | ${PACKAGE_NAME} 14 | SHARED 15 | src/main/cpp/cpp-adapter.cpp 16 | ../cpp/RNMultithreadingInstaller.cpp 17 | ../cpp/ThreadPool.cpp 18 | ) 19 | 20 | # includes 21 | 22 | file (GLOB LIBFBJNI_INCLUDE_DIR "${BUILD_DIR}/fbjni-*-headers.jar/") 23 | 24 | target_include_directories( 25 | ${PACKAGE_NAME} 26 | PRIVATE 27 | ${LIBFBJNI_INCLUDE_DIR} 28 | ${BUILD_DIR}/third-party-ndk/boost 29 | ${BUILD_DIR}/third-party-ndk/double-conversion 30 | ${BUILD_DIR}/third-party-ndk/folly 31 | ${BUILD_DIR}/third-party-ndk/glog 32 | "${NODE_MODULES_DIR}/react-native/React" 33 | "${NODE_MODULES_DIR}/react-native/React/Base" 34 | "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni" 35 | "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni" 36 | "${NODE_MODULES_DIR}/react-native/ReactCommon" 37 | "${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker" 38 | "${NODE_MODULES_DIR}/react-native/ReactCommon/jsi" 39 | "${NODE_MODULES_DIR}/react-native/ReactCommon/jsi" 40 | "${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/headers/Tools" 41 | "${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/headers/SpecTools" 42 | "${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/headers/SharedItems" 43 | "${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/headers/Registries" 44 | "${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/hidden_headers" 45 | "../cpp" 46 | ) 47 | 48 | # find libraries 49 | 50 | file (GLOB LIBRN_DIR "${BUILD_DIR}/react-native-0*/jni/${ANDROID_ABI}") 51 | file (GLOB LIBJSC_DIR "${BUILD_DIR}/android-jsc*.aar/jni/${ANDROID_ABI}") 52 | file (GLOB LIBREANIMATED_DIR "${BUILD_DIR}/react-native-reanimated-*.aar/jni/${ANDROID_ABI}") 53 | 54 | 55 | find_library( 56 | LOG_LIB 57 | log 58 | ) 59 | find_library( 60 | FBJNI_LIB 61 | fbjni 62 | PATHS ${LIBRN_DIR} 63 | NO_CMAKE_FIND_ROOT_PATH 64 | ) 65 | find_library( 66 | REANIMATED_LIB 67 | reanimated 68 | PATHS ${LIBREANIMATED_DIR} 69 | NO_CMAKE_FIND_ROOT_PATH 70 | ) 71 | 72 | find_library( 73 | REACT_NATIVE_JNI_LIB 74 | reactnativejni 75 | PATHS ${LIBRN_DIR} 76 | NO_CMAKE_FIND_ROOT_PATH 77 | ) 78 | 79 | find_library( 80 | REACT_NATIVE_UTILS_LIB 81 | reactnativeutilsjni 82 | PATHS ${LIBRN_DIR} 83 | NO_CMAKE_FIND_ROOT_PATH 84 | ) 85 | 86 | # linking 87 | 88 | find_library( 89 | FBJNI_LIBRARY fbjni 90 | PATHS ${libfbjni_link_DIRS} 91 | NO_CMAKE_FIND_ROOT_PATH 92 | ) 93 | 94 | # build shared lib 95 | 96 | target_link_libraries( 97 | ${PACKAGE_NAME} 98 | ${LOG_LIB} 99 | ${REANIMATED_LIB} 100 | ${REACT_NATIVE_JNI_LIB} 101 | ${REACT_NATIVE_UTILS_LIB} 102 | ${FBJNI_LIB} 103 | android 104 | ) 105 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonSlurper 2 | import org.apache.tools.ant.filters.ReplaceTokens 3 | 4 | buildscript { 5 | // The Android Gradle plugin is only required when opening the android folder stand-alone. 6 | // This avoids unnecessary downloads and potential conflicts when the library is included as a 7 | // module dependency in an application project. 8 | //if (project == rootProject) { 9 | repositories { 10 | google() 11 | jcenter() 12 | maven { 13 | url "https://plugins.gradle.org/m2/" 14 | } 15 | } 16 | dependencies { 17 | classpath 'com.android.tools.build:gradle:4.1.3' 18 | classpath "de.undercouch:gradle-download-task:4.1.1" 19 | } 20 | //} 21 | } 22 | 23 | apply plugin: 'com.android.library' 24 | apply plugin: "de.undercouch.download" 25 | 26 | def getExtOrDefault(name, defaultValue) { 27 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : defaultValue 28 | } 29 | 30 | android { 31 | compileSdkVersion getExtOrDefault('compileSdkVersion', 28) 32 | 33 | defaultConfig { 34 | minSdkVersion getExtOrDefault('minSdkVersion', 16) 35 | targetSdkVersion getExtOrDefault('targetSdkVersion', 28) 36 | 37 | externalNativeBuild { 38 | cmake { 39 | cppFlags "-fexceptions", "-frtti", "-std=c++1y", "-DONANDROID" 40 | abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' 41 | arguments '-DANDROID_STL=c++_shared', "-DNODE_MODULES_DIR=${rootDir}/../node_modules" 42 | } 43 | } 44 | } 45 | 46 | lintOptions{ 47 | abortOnError false 48 | } 49 | 50 | externalNativeBuild { 51 | cmake { 52 | path "CMakeLists.txt" 53 | } 54 | } 55 | 56 | packagingOptions { 57 | excludes = ["**/libc++_shared.so"] 58 | } 59 | 60 | buildFeatures { 61 | prefab true 62 | } 63 | 64 | configurations { 65 | extractHeaders 66 | extractJNI 67 | } 68 | } 69 | 70 | repositories { 71 | google() 72 | maven { 73 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 74 | url "$rootDir/../node_modules/react-native/android" 75 | } 76 | jcenter() 77 | } 78 | 79 | dependencies { 80 | //noinspection GradleDynamicVersion 81 | implementation 'com.facebook.react:react-native:+' 82 | implementation project(':react-native-reanimated') 83 | 84 | //noinspection GradleDynamicVersion 85 | extractHeaders("com.facebook.fbjni:fbjni:+:headers") 86 | //noinspection GradleDynamicVersion 87 | extractJNI("com.facebook.fbjni:fbjni:+") 88 | 89 | def rnAAR = fileTree("${rootDir}/../node_modules/react-native/android").matching({ it.include "**/**/*.aar" }).singleFile 90 | def jscAAR = fileTree("${rootDir}/../node_modules/jsc-android/dist/org/webkit/android-jsc").matching({ it.include "**/**/*.aar" }).singleFile 91 | 92 | def inputFile = new File(rootDir, '../node_modules/react-native/package.json') 93 | def json = new JsonSlurper().parseText(inputFile.text) 94 | def reactNativeVersion = json.version as String 95 | def (major, minor, patch) = reactNativeVersion.tokenize('.') 96 | def raAAR = "${rootDir}/../node_modules/react-native-reanimated/android/react-native-reanimated-${minor}.aar" 97 | 98 | extractJNI(files(rnAAR, jscAAR, raAAR)) 99 | } 100 | 101 | // fbjni 102 | // https://github.com/facebookincubator/fbjni/blob/master/docs/android_setup.md 103 | 104 | task extractAARHeaders { 105 | doLast { 106 | configurations.extractHeaders.files.each { 107 | def file = it.absoluteFile 108 | copy { 109 | from zipTree(file) 110 | into "$buildDir/$file.name" 111 | include "**/*.h" 112 | } 113 | } 114 | } 115 | } 116 | 117 | task extractJNIFiles { 118 | doLast { 119 | configurations.extractJNI.files.each { 120 | def file = it.absoluteFile 121 | 122 | copy { 123 | from zipTree(file) 124 | into "$buildDir/$file.name" 125 | include "jni/**/*" 126 | } 127 | } 128 | } 129 | } 130 | 131 | // third-party-ndk deps headers 132 | // mostly a copy of https://github.com/software-mansion/react-native-reanimated/blob/master/android/build.gradle#L115 133 | 134 | def downloadsDir = new File("$buildDir/downloads") 135 | def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") 136 | def thirdPartyVersionsFile = new File("${rootDir}/../node_modules/react-native/ReactAndroid/gradle.properties") 137 | def thirdPartyVersions = new Properties() 138 | thirdPartyVersions.load(new FileInputStream(thirdPartyVersionsFile)) 139 | 140 | def BOOST_VERSION = thirdPartyVersions["BOOST_VERSION"] 141 | def boost_file = new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz") 142 | def DOUBLE_CONVERSION_VERSION = thirdPartyVersions["DOUBLE_CONVERSION_VERSION"] 143 | def double_conversion_file = new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz") 144 | def FOLLY_VERSION = thirdPartyVersions["FOLLY_VERSION"] 145 | def folly_file = new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz") 146 | def GLOG_VERSION = thirdPartyVersions["GLOG_VERSION"] 147 | def glog_file = new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz") 148 | 149 | task createNativeDepsDirectories { 150 | downloadsDir.mkdirs() 151 | thirdPartyNdkDir.mkdirs() 152 | } 153 | 154 | task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { 155 | src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz") 156 | onlyIfNewer(true) 157 | overwrite(false) 158 | dest(boost_file) 159 | } 160 | 161 | task prepareBoost(dependsOn: downloadBoost, type: Copy) { 162 | from(tarTree(resources.gzip(downloadBoost.dest))) 163 | from("src/main/jni/third-party/boost/Android.mk") 164 | include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp") 165 | includeEmptyDirs = false 166 | into("$thirdPartyNdkDir") // /boost_X_XX_X 167 | doLast { 168 | file("$thirdPartyNdkDir/boost_${BOOST_VERSION}").renameTo("$thirdPartyNdkDir/boost") 169 | } 170 | } 171 | 172 | task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) { 173 | src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz") 174 | onlyIfNewer(true) 175 | overwrite(false) 176 | dest(double_conversion_file) 177 | } 178 | 179 | task prepareDoubleConversion(dependsOn: downloadDoubleConversion, type: Copy) { 180 | from(tarTree(downloadDoubleConversion.dest)) 181 | from("src/main/jni/third-party/double-conversion/Android.mk") 182 | include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk") 183 | filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" }) 184 | includeEmptyDirs = false 185 | into("$thirdPartyNdkDir/double-conversion") 186 | } 187 | 188 | task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) { 189 | src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz") 190 | onlyIfNewer(true) 191 | overwrite(false) 192 | dest(folly_file) 193 | } 194 | 195 | task prepareFolly(dependsOn: downloadFolly, type: Copy) { 196 | from(tarTree(downloadFolly.dest)) 197 | from("src/main/jni/third-party/folly/Android.mk") 198 | include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk") 199 | eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") } 200 | includeEmptyDirs = false 201 | into("$thirdPartyNdkDir/folly") 202 | } 203 | 204 | task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { 205 | src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz") 206 | onlyIfNewer(true) 207 | overwrite(false) 208 | dest(glog_file) 209 | } 210 | 211 | task prepareGlog(dependsOn: downloadGlog, type: Copy) { 212 | from(tarTree(downloadGlog.dest)) 213 | from("src/main/jni/third-party/glog/") 214 | include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h") 215 | includeEmptyDirs = false 216 | filesMatching("**/*.h.in") { 217 | filter(ReplaceTokens, tokens: [ 218 | ac_cv_have_unistd_h : "1", 219 | ac_cv_have_stdint_h : "1", 220 | ac_cv_have_systypes_h : "1", 221 | ac_cv_have_inttypes_h : "1", 222 | ac_cv_have_libgflags : "0", 223 | ac_google_start_namespace : "namespace google {", 224 | ac_cv_have_uint16_t : "1", 225 | ac_cv_have_u_int16_t : "1", 226 | ac_cv_have___uint16 : "0", 227 | ac_google_end_namespace : "}", 228 | ac_cv_have___builtin_expect : "1", 229 | ac_google_namespace : "google", 230 | ac_cv___attribute___noinline : "__attribute__ ((noinline))", 231 | ac_cv___attribute___noreturn : "__attribute__ ((noreturn))", 232 | ac_cv___attribute___printf_4_5: "__attribute__((__format__ (__printf__, 4, 5)))" 233 | ]) 234 | it.path = (it.name - ".in") 235 | } 236 | into("$thirdPartyNdkDir/glog") 237 | 238 | doLast { 239 | copy { 240 | from(fileTree(dir: "$thirdPartyNdkDir/glog", includes: ["stl_logging.h", "logging.h", "raw_logging.h", "vlog_is_on.h", "**/src/glog/log_severity.h"]).files) 241 | includeEmptyDirs = false 242 | into("$thirdPartyNdkDir/glog/exported/glog") 243 | } 244 | } 245 | } 246 | 247 | task prepareThirdPartyNdkHeaders { 248 | if (!boost_file.exists()) { 249 | dependsOn(prepareBoost) 250 | } 251 | if (!double_conversion_file.exists()) { 252 | dependsOn(prepareDoubleConversion) 253 | } 254 | if (!folly_file.exists()) { 255 | dependsOn(prepareFolly) 256 | } 257 | if (!glog_file.exists()) { 258 | dependsOn(prepareGlog) 259 | } 260 | } 261 | 262 | // pre-native build pipeline 263 | 264 | tasks.whenTaskAdded { task -> 265 | if (task.name.contains('externalNativeBuild')) { 266 | task.dependsOn(extractAARHeaders) 267 | task.dependsOn(extractJNIFiles) 268 | task.dependsOn(prepareThirdPartyNdkHeaders) 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | Multithreading_compileSdkVersion=29 2 | Multithreading_buildToolsVersion=29.0.2 3 | Multithreading_targetSdkVersion=29 4 | android.useAndroidX=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Apr 12 19:56:15 CEST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'rnmultithreading' 2 | 3 | include ':react-native-reanimated' 4 | project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android/') 5 | 6 | include ':rnmultithreading' 7 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/cpp/AndroidErrorHandler.h: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/software-mansion/react-native-reanimated/blob/master/android/src/main/cpp/headers/AndroidScheduler.h 2 | 3 | #pragma once 4 | 5 | #include "ErrorHandler.h" 6 | #include "AndroidScheduler.h" 7 | #include "Scheduler.h" 8 | #include 9 | #include 10 | #include 11 | #include "Logger.h" 12 | 13 | namespace reanimated 14 | { 15 | 16 | class AndroidErrorHandler : public JavaClass, public ErrorHandler { 17 | std::shared_ptr error; 18 | std::shared_ptr scheduler; 19 | void raiseSpec() override; 20 | public: 21 | static auto constexpr kJavaDescriptor = "Lcom/swmansion/reanimated/AndroidErrorHandler;"; 22 | AndroidErrorHandler( 23 | std::shared_ptr scheduler); 24 | std::shared_ptr getScheduler() override; 25 | std::shared_ptr getError() override; 26 | void setError(std::string message) override; 27 | virtual ~AndroidErrorHandler() {} 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /android/src/main/cpp/AndroidScheduler.h: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/software-mansion/react-native-reanimated/blob/master/android/src/main/cpp/headers/AndroidScheduler.h 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Scheduler.h" 11 | 12 | namespace reanimated { 13 | 14 | using namespace facebook; 15 | 16 | class AndroidScheduler : public jni::HybridClass { 17 | public: 18 | static auto constexpr kJavaDescriptor = "Lcom/swmansion/reanimated/Scheduler;"; 19 | static jni::local_ref initHybrid(jni::alias_ref jThis); 20 | static void registerNatives(); 21 | 22 | std::shared_ptr getScheduler() { return scheduler_; } 23 | 24 | void scheduleOnUI(); 25 | 26 | private: 27 | friend HybridBase; 28 | 29 | void triggerUI(); 30 | 31 | jni::global_ref javaPart_; 32 | std::shared_ptr scheduler_; 33 | 34 | explicit AndroidScheduler(jni::alias_ref jThis); 35 | }; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /android/src/main/cpp/cpp-adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "RNMultithreadingInstaller.h" 10 | 11 | #include "Scheduler.h" 12 | #include "AndroidErrorHandler.h" 13 | #include "AndroidScheduler.h" 14 | 15 | using namespace facebook; 16 | using namespace reanimated; 17 | 18 | 19 | struct MultithreadingModule : jni::JavaClass { 20 | public: 21 | __unused static constexpr auto kJavaDescriptor = "Lcom/reactnativemultithreading/MultithreadingModule;"; 22 | 23 | static constexpr auto TAG = "RNMultithreading"; 24 | 25 | static void registerNatives() { 26 | javaClassStatic()->registerNatives({ 27 | makeNativeMethod("installNative", 28 | MultithreadingModule::installNative) 29 | }); 30 | } 31 | 32 | private: 33 | static std::shared_ptr makeJSExecutorFactory() { 34 | __android_log_write(ANDROID_LOG_INFO, TAG, "Calling Java method MultithreadingModule.makeJSExecutor()..."); 35 | static const auto cls = javaClassStatic(); 36 | static const auto method = cls->getStaticMethod("makeJSExecutor"); 37 | auto result = method(cls); 38 | __android_log_write(ANDROID_LOG_INFO, TAG, "JavaScriptExecutor created! Getting factory..."); 39 | return result->cthis()->getExecutorFactory(); 40 | } 41 | 42 | static void installNative(jni::alias_ref, 43 | jlong jsiRuntimePointer, 44 | jni::alias_ref jsCallInvokerHolder, 45 | jni::alias_ref androidScheduler) { 46 | 47 | auto runtime = reinterpret_cast(jsiRuntimePointer); 48 | 49 | auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker(); 50 | auto scheduler = androidScheduler->cthis()->getScheduler(); 51 | scheduler->setJSCallInvoker(jsCallInvoker); 52 | 53 | auto makeScheduler = [scheduler]() -> std::shared_ptr { 54 | return scheduler; 55 | }; 56 | auto makeErrorHandler = [](const std::shared_ptr& scheduler_) -> std::shared_ptr { 57 | return std::make_shared(scheduler_); 58 | }; 59 | auto makeJsExecutor = []() -> std::unique_ptr { 60 | __android_log_write(ANDROID_LOG_INFO, TAG, "Creating JSExecutorFactory.."); 61 | try { 62 | std::shared_ptr delegate = std::shared_ptr(); 63 | std::shared_ptr jsQueue = std::shared_ptr(); 64 | 65 | auto jsExecutorFactory = makeJSExecutorFactory(); 66 | __android_log_write(ANDROID_LOG_INFO, TAG, "Creating JSExecutor.."); 67 | auto executor = jsExecutorFactory->createJSExecutor(delegate, 68 | jsQueue); 69 | auto runtimePointer = static_cast(executor->getJavaScriptContext()); 70 | __android_log_write(ANDROID_LOG_INFO, TAG, "JSExecutor created!"); 71 | 72 | // I need to release the local shared_ptr because otherwise the returned jsi::Runtime will be destroyed immediately. 73 | auto _ = executor.release(); 74 | 75 | return std::unique_ptr(runtimePointer); 76 | } catch (std::exception& exc) { 77 | // Fatal error - the runtime can't be created at all. 78 | __android_log_write(ANDROID_LOG_ERROR, TAG, "Failed to create JSExecutor!"); 79 | __android_log_write(ANDROID_LOG_ERROR, TAG, exc.what()); 80 | abort(); 81 | } 82 | }; 83 | mrousavy::multithreading::install(*runtime, makeJsExecutor, makeScheduler, makeErrorHandler); 84 | 85 | } 86 | }; 87 | 88 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { 89 | return facebook::jni::initialize(vm, [] { 90 | MultithreadingModule::registerNatives(); 91 | }); 92 | } 93 | /* 94 | To create the Scheduler/AndroidErrorHandler: 95 | 1.: #include 96 | 2.: class AndroidScheduler : public jni::HybridClass; 97 | 3.: jni::alias_ref androidScheduler 98 | 4.: api project(":react-native-reanimated") 99 | */ -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativemultithreading/MultithreadingJSIModulePackage.java: -------------------------------------------------------------------------------- 1 | package com.reactnativemultithreading; 2 | 3 | import com.facebook.react.bridge.JSIModuleSpec; 4 | import com.facebook.react.bridge.JavaScriptContextHolder; 5 | import com.facebook.react.bridge.ReactApplicationContext; 6 | import com.swmansion.reanimated.ReanimatedJSIModulePackage; 7 | 8 | import java.util.List; 9 | 10 | public class MultithreadingJSIModulePackage extends ReanimatedJSIModulePackage { 11 | @Override 12 | public List getJSIModules(ReactApplicationContext reactApplicationContext, JavaScriptContextHolder jsContext) { 13 | try { 14 | return super.getJSIModules(reactApplicationContext, jsContext); 15 | } finally { 16 | MultithreadingModule.install(reactApplicationContext, jsContext); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativemultithreading/MultithreadingModule.java: -------------------------------------------------------------------------------- 1 | package com.reactnativemultithreading; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.annotation.Keep; 6 | import androidx.annotation.NonNull; 7 | 8 | import com.facebook.hermes.reactexecutor.HermesExecutorFactory; 9 | import com.facebook.proguard.annotations.DoNotStrip; 10 | import com.facebook.react.bridge.JavaScriptContextHolder; 11 | import com.facebook.react.bridge.JavaScriptExecutor; 12 | import com.facebook.react.bridge.JavaScriptExecutorFactory; 13 | import com.facebook.react.bridge.ReactApplicationContext; 14 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 15 | import com.facebook.react.jscexecutor.JSCExecutorFactory; 16 | import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; 17 | import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder; 18 | import com.facebook.soloader.SoLoader; 19 | import com.swmansion.reanimated.Scheduler; 20 | 21 | @Keep 22 | @DoNotStrip 23 | public class MultithreadingModule extends ReactContextBaseJavaModule { 24 | static { 25 | System.loadLibrary("reanimated"); 26 | System.loadLibrary("rnmultithreading"); 27 | } 28 | 29 | static String TAG = "RNMultithreading"; 30 | 31 | // Dummy so react native adds it to the Gradle Module System 32 | public MultithreadingModule(ReactApplicationContext context) { 33 | super(context); 34 | } 35 | 36 | @NonNull 37 | @Override 38 | public String getName() { 39 | return TAG; 40 | } 41 | 42 | private static native void installNative(long jsiRuntimePointer, 43 | CallInvokerHolderImpl jsCallInvokerHolder, 44 | Scheduler scheduler); 45 | 46 | public static void install(ReactApplicationContext context, JavaScriptContextHolder jsContext) { 47 | CallInvokerHolderImpl holder = (CallInvokerHolderImpl) context.getCatalystInstance().getJSCallInvokerHolder(); 48 | SoLoader.init(context, false); // <-- required for makeJSExecutorFactory later on 49 | installNative(jsContext.get(), holder, new Scheduler(context)); 50 | } 51 | 52 | 53 | // Called from the C++ code 54 | @SuppressWarnings({"unused", "RedundantSuppression"}) 55 | public static JavaScriptExecutor makeJSExecutor() { 56 | Log.i(TAG, "Creating JavaScriptExecutorFactory..."); 57 | JavaScriptExecutorFactory factory = makeJSExecutorFactory(); 58 | try { 59 | Log.i(TAG, "Factory created! Creating JavaScriptExecutor..."); 60 | return factory.create(); 61 | } catch (Exception e) { 62 | Log.e(TAG, "Failed to create JavaScriptExecutor!"); 63 | e.printStackTrace(); 64 | return null; 65 | } 66 | } 67 | 68 | // method from React native 69 | public static JavaScriptExecutorFactory makeJSExecutorFactory() { 70 | try { 71 | Log.i(TAG, "Trying to create JSC Factory..."); 72 | SoLoader.loadLibrary("jscexecutor"); 73 | return new JSCExecutorFactory("Multithreading", "Multithreading"); 74 | } catch (UnsatisfiedLinkError jscE) { 75 | // https://github.com/facebook/hermes/issues/78 shows that 76 | // people who aren't trying to use Hermes are having issues. 77 | // https://github.com/facebook/react-native/issues/25923#issuecomment-554295179 78 | // includes the actual JSC error in at least one case. 79 | // 80 | // So, if "__cxa_bad_typeid" shows up in the jscE exception 81 | // message, then we will assume that's the failure and just 82 | // throw now. 83 | 84 | if (jscE.getMessage().contains("__cxa_bad_typeid")) { 85 | throw jscE; 86 | } 87 | 88 | // Otherwise use Hermes 89 | try { 90 | Log.i(TAG, "Trying to create Hermes Factory..."); 91 | return new HermesExecutorFactory(); 92 | } catch (UnsatisfiedLinkError hermesE) { 93 | // If we get here, either this is a JSC build, and of course 94 | // Hermes failed (since it's not in the APK), or it's a Hermes 95 | // build, and Hermes had a problem. 96 | 97 | // We suspect this is a JSC issue (it's the default), so we 98 | // will throw that exception, but we will print hermesE first, 99 | // since it could be a Hermes issue and we don't want to 100 | // swallow that. 101 | hermesE.printStackTrace(); 102 | throw jscE; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativemultithreading/MultithreadingPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactnativemultithreading; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class MultithreadingPackage implements ReactPackage { 14 | @NonNull 15 | @Override 16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 17 | return Collections.singletonList((NativeModule)new MultithreadingModule(reactContext)); 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 23 | return Collections.emptyList(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /cpp/MakeJSIRuntime.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MakeJSIRuntime.cpp 3 | // Multithreading 4 | // 5 | // Created by Marc Rousavy on 02.04.21. 6 | // Copyright © 2021 Facebook. All rights reserved. 7 | // 8 | 9 | #include "MakeJSIRuntime.h" 10 | 11 | #include 12 | 13 | 14 | #if __has_include() 15 | // Hermes (https://hermesengine.dev) 16 | #include 17 | #elif __has_include() 18 | // V8 (https://github.com/Kudo/react-native-v8) 19 | #include 20 | #else 21 | // JSC (default) 22 | #include 23 | #endif 24 | 25 | using namespace facebook; 26 | 27 | namespace mrousavy { 28 | namespace multithreading { 29 | 30 | std::unique_ptr makeJSIRuntime() { 31 | #if __has_include() 32 | return facebook::hermes::makeHermesRuntime(); 33 | #elif __has_include() 34 | return facebook::createV8Runtime(""); 35 | #else 36 | return facebook::jsc::makeJSCRuntime(); 37 | #endif 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /cpp/MakeJSIRuntime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace facebook; 7 | 8 | namespace mrousavy { 9 | namespace multithreading { 10 | 11 | /** 12 | * Create a new jsi::Runtime with the configured JS engine. 13 | * 14 | * Supported Engines: 15 | * 16 | * - Hermes 17 | * - V8 18 | * - JavaScriptCore 19 | */ 20 | std::unique_ptr makeJSIRuntime(); 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cpp/RNMultithreadingInstaller.cpp: -------------------------------------------------------------------------------- 1 | #include "RNMultithreadingInstaller.h" 2 | #include 3 | #include "ThreadPool.h" 4 | #include 5 | 6 | #if __has_include() 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #else 13 | #include "Scheduler.h" 14 | #include "ShareableValue.h" 15 | #include "RuntimeManager.h" 16 | #include "RuntimeDecorator.h" 17 | #include "ErrorHandler.h" 18 | #endif 19 | 20 | #ifdef ON_ANDROID 21 | #include 22 | #endif 23 | 24 | namespace mrousavy { 25 | namespace multithreading { 26 | 27 | std::unique_ptr manager; 28 | 29 | void install(jsi::Runtime& runtime, 30 | const std::function()>& makeRuntime, 31 | const std::function()>& makeScheduler, 32 | const std::function(std::shared_ptr)>& makeErrorHandler) { 33 | auto pool = std::make_shared(1); 34 | 35 | // Quickly setup the runtime - this is executed in parallel so we have to join this on the JS thread if spawnThread is called before this finishes. 36 | auto setupFutureSingle = pool->enqueue([makeScheduler, makeRuntime, makeErrorHandler]() { 37 | #ifdef ON_ANDROID 38 | // We need to attach this Thread to JNI because the Runtime is a HybridClass. 39 | jni::ThreadScope::WithClassLoader([makeRuntime, makeScheduler, makeErrorHandler]() { 40 | __unused jni::ThreadScope scope; 41 | #endif 42 | auto runtime = makeRuntime(); 43 | reanimated::RuntimeDecorator::decorateRuntime(*runtime, "CUSTOM_THREAD_1"); 44 | auto scheduler = makeScheduler(); 45 | auto errorHandler = makeErrorHandler(scheduler); 46 | manager = std::make_unique(std::move(runtime), 47 | errorHandler, 48 | scheduler); 49 | #ifdef ON_ANDROID 50 | }); 51 | #endif 52 | }); 53 | auto setupFuture = std::make_shared>(std::move(setupFutureSingle)); 54 | 55 | // spawnThread(run: () => T): Promise 56 | auto spawnThread = jsi::Function::createFromHostFunction(runtime, 57 | jsi::PropNameID::forAscii(runtime, "spawnThread"), 58 | 1, // run 59 | [setupFuture, pool](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { 60 | if (!arguments[0].isObject()) 61 | throw jsi::JSError(runtime, "spawnThread: First argument has to be a function!"); 62 | 63 | if (setupFuture->valid()) 64 | setupFuture->get(); // clears future, makes invalid 65 | 66 | auto worklet = reanimated::ShareableValue::adapt(runtime, arguments[0], manager.get()); 67 | 68 | auto spawnThreadCallback = jsi::Function::createFromHostFunction(runtime, 69 | jsi::PropNameID::forAscii(runtime, "spawnThreadCallback"), 70 | 2, 71 | [worklet, pool](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { 72 | auto resolverValue = std::make_shared((arguments[0].asObject(runtime))); 73 | auto rejecterValue = std::make_shared((arguments[1].asObject(runtime))); 74 | 75 | auto resolver = [&runtime, resolverValue](const std::shared_ptr& shareableValue) { 76 | manager->scheduler->scheduleOnJS([&runtime, resolverValue, shareableValue] () { 77 | resolverValue->asObject(runtime).asFunction(runtime).call(runtime, shareableValue->getValue(runtime)); 78 | }); 79 | }; 80 | auto rejecter = [&runtime, rejecterValue](const std::string& message) { 81 | manager->scheduler->scheduleOnJS([&runtime, rejecterValue, message] () { 82 | rejecterValue->asObject(runtime).asFunction(runtime).call(runtime, jsi::JSError(runtime, message).value()); 83 | }); 84 | }; 85 | 86 | pool->enqueue([resolver, rejecter, worklet]() { 87 | try { 88 | auto& runtime = *manager->runtime; 89 | 90 | auto function = worklet->getValue(runtime).asObject(runtime).asFunction(runtime); 91 | auto result = function.getFunction(runtime).callWithThis(runtime, function); 92 | 93 | auto shareableResult = reanimated::ShareableValue::adapt(runtime, result, manager.get()); 94 | resolver(shareableResult); 95 | } catch (std::exception& exc) { 96 | rejecter(exc.what()); 97 | } 98 | }); 99 | return jsi::Value::undefined(); 100 | }); 101 | 102 | auto newPromise = runtime.global().getProperty(runtime, "Promise"); 103 | auto promise = newPromise 104 | .asObject(runtime) 105 | .asFunction(runtime) 106 | .callAsConstructor(runtime, spawnThreadCallback); 107 | 108 | return promise; 109 | }); 110 | runtime.global().setProperty(runtime, "spawnThread", std::move(spawnThread)); 111 | } 112 | 113 | } // namespace multithreading 114 | } // namespace mrousavy 115 | -------------------------------------------------------------------------------- /cpp/RNMultithreadingInstaller.h: -------------------------------------------------------------------------------- 1 | // C mrousavy 2 | 3 | #pragma once 4 | #include 5 | 6 | #if __has_include() 7 | #include 8 | #include 9 | #else 10 | #include "Scheduler.h" 11 | #include "ErrorHandler.h" 12 | #endif 13 | 14 | using namespace facebook; 15 | 16 | namespace mrousavy { 17 | namespace multithreading { 18 | 19 | void install(jsi::Runtime& runtime, 20 | const std::function()>& makeRuntime, 21 | const std::function()>& makeScheduler, 22 | const std::function(std::shared_ptr)>& makeErrorHandler); 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cpp/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadPool.cpp 3 | // Multithreading 4 | // 5 | // Created by Marc Rousavy on 16.03.21. 6 | // Copyright © 2021 Facebook. All rights reserved. 7 | // 8 | 9 | #include "ThreadPool.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace mrousavy { 23 | namespace multithreading { 24 | 25 | // the constructor just launches some amount of workers 26 | ThreadPool::ThreadPool(size_t threads): stop(false) { 27 | for (size_t i = 0; i < threads; ++i) { 28 | workers.emplace_back([this] { 29 | while (true) { 30 | std::function task; 31 | 32 | { 33 | std::unique_lock lock(this->queue_mutex); 34 | this->condition.wait(lock, 35 | [this]{ return this->stop || !this->tasks.empty(); }); 36 | if(this->stop && this->tasks.empty()) 37 | return; 38 | task = std::move(this->tasks.front()); 39 | this->tasks.pop(); 40 | } 41 | 42 | task(); 43 | } 44 | }); 45 | } 46 | } 47 | 48 | // add new work item to the pool 49 | std::future ThreadPool::enqueue(std::function func) { 50 | auto task = std::make_shared>(std::bind(std::forward>(func))); 51 | std::future res = task->get_future(); 52 | 53 | { 54 | std::unique_lock lock(queue_mutex); 55 | 56 | // don't allow enqueueing after stopping the pool 57 | if (stop) 58 | throw std::runtime_error("enqueue on stopped ThreadPool"); 59 | 60 | tasks.emplace([task](){ (*task)(); }); 61 | } 62 | 63 | condition.notify_one(); 64 | return res; 65 | } 66 | 67 | // the destructor joins all threads 68 | ThreadPool::~ThreadPool() { 69 | { 70 | std::unique_lock lock(queue_mutex); 71 | stop = true; 72 | } 73 | condition.notify_all(); 74 | for (auto& worker : workers) { 75 | worker.join(); 76 | } 77 | } 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /cpp/ThreadPool.h: -------------------------------------------------------------------------------- 1 | // Source: https://github.com/progschj/ThreadPool 2 | 3 | #ifndef THREAD_POOL_H 4 | #define THREAD_POOL_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace mrousavy { 11 | namespace multithreading { 12 | 13 | class ThreadPool { 14 | public: 15 | ThreadPool(size_t threadCount); 16 | std::future enqueue(std::function task); 17 | ~ThreadPool(); 18 | private: 19 | // need to keep track of threads so we can join them 20 | std::vector workers; 21 | // the task queue 22 | std::queue> tasks; 23 | 24 | // synchronization 25 | std::mutex queue_mutex; 26 | std::condition_variable condition; 27 | bool stop; 28 | }; 29 | 30 | 31 | } 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /example/android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 22 | * bundleCommand: "ram-bundle", 23 | * 24 | * // whether to bundle JS and assets in debug mode 25 | * bundleInDebug: false, 26 | * 27 | * // whether to bundle JS and assets in release mode 28 | * bundleInRelease: true, 29 | * 30 | * // whether to bundle JS and assets in another build variant (if configured). 31 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 32 | * // The configuration property can be in the following formats 33 | * // 'bundleIn${productFlavor}${buildType}' 34 | * // 'bundleIn${buildType}' 35 | * // bundleInFreeDebug: true, 36 | * // bundleInPaidRelease: true, 37 | * // bundleInBeta: true, 38 | * 39 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 40 | * // for MultithreadingExample: to disable dev mode in the staging build type (if configured) 41 | * devDisabledInStaging: true, 42 | * // The configuration property can be in the following formats 43 | * // 'devDisabledIn${productFlavor}${buildType}' 44 | * // 'devDisabledIn${buildType}' 45 | * 46 | * // the root of your project, i.e. where "package.json" lives 47 | * root: "../../", 48 | * 49 | * // where to put the JS bundle asset in debug mode 50 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 51 | * 52 | * // where to put the JS bundle asset in release mode 53 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 54 | * 55 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 56 | * // require('./image.png')), in debug mode 57 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 58 | * 59 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 60 | * // require('./image.png')), in release mode 61 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 62 | * 63 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 64 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 65 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 66 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 67 | * // for MultithreadingExample, you might want to remove it from here. 68 | * inputExcludes: ["android/**", "ios/**"], 69 | * 70 | * // override which node gets called and with what additional arguments 71 | * nodeExecutableAndArgs: ["node"], 72 | * 73 | * // supply additional arguments to the packager 74 | * extraPackagerArgs: [] 75 | * ] 76 | */ 77 | 78 | project.ext.react = [ 79 | enableHermes: false, // clean and rebuild if changing 80 | entryFile: "index.js", 81 | ] 82 | 83 | apply from: "../../node_modules/react-native/react.gradle" 84 | 85 | /** 86 | * Set this to true to create two separate APKs instead of one: 87 | * - An APK that only works on ARM devices 88 | * - An APK that only works on x86 devices 89 | * The advantage is the size of the APK is reduced by about 4MB. 90 | * Upload all the APKs to the Play Store and people will download 91 | * the correct one based on the CPU architecture of their device. 92 | */ 93 | def enableSeparateBuildPerCPUArchitecture = false 94 | 95 | /** 96 | * Run Proguard to shrink the Java bytecode in release builds. 97 | */ 98 | def enableProguardInReleaseBuilds = false 99 | 100 | /** 101 | * The preferred build flavor of JavaScriptCore. 102 | * 103 | * For MultithreadingExample, to use the international variant, you can use: 104 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 105 | * 106 | * The international variant includes ICU i18n library and necessary data 107 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 108 | * give correct results when using with locales other than en-US. Note that 109 | * this variant is about 6MiB larger per architecture than default. 110 | */ 111 | def jscFlavor = 'org.webkit:android-jsc:+' 112 | 113 | /** 114 | * Whether to enable the Hermes VM. 115 | * 116 | * This should be set on project.ext.react and mirrored here. If it is not set 117 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 118 | * and the benefits of using Hermes will therefore be sharply reduced. 119 | */ 120 | def enableHermes = project.ext.react.get("enableHermes", false); 121 | 122 | android { 123 | ndkVersion rootProject.ext.ndkVersion 124 | compileSdkVersion rootProject.ext.compileSdkVersion 125 | 126 | compileOptions { 127 | sourceCompatibility JavaVersion.VERSION_1_8 128 | targetCompatibility JavaVersion.VERSION_1_8 129 | } 130 | 131 | defaultConfig { 132 | applicationId "com.example.reactnativemultithreading" 133 | minSdkVersion rootProject.ext.minSdkVersion 134 | targetSdkVersion rootProject.ext.targetSdkVersion 135 | versionCode 1 136 | versionName "1.0" 137 | } 138 | splits { 139 | abi { 140 | reset() 141 | enable enableSeparateBuildPerCPUArchitecture 142 | universalApk false // If true, also generate a universal APK 143 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 144 | } 145 | } 146 | signingConfigs { 147 | debug { 148 | storeFile file('debug.keystore') 149 | storePassword 'android' 150 | keyAlias 'androiddebugkey' 151 | keyPassword 'android' 152 | } 153 | } 154 | buildTypes { 155 | debug { 156 | signingConfig signingConfigs.debug 157 | } 158 | release { 159 | // Caution! In production, you need to generate your own keystore file. 160 | // see https://reactnative.dev/docs/signed-apk-android. 161 | signingConfig signingConfigs.debug 162 | minifyEnabled enableProguardInReleaseBuilds 163 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 164 | } 165 | } 166 | // applicationVariants are e.g. debug, release 167 | applicationVariants.all { variant -> 168 | variant.outputs.each { output -> 169 | // For each separate APK per architecture, set a unique version code as described here: 170 | // https://developer.android.com/studio/build/configure-apk-splits.html 171 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 172 | def abi = output.getFilter(OutputFile.ABI) 173 | if (abi != null) { // null for the universal-debug, universal-release variants 174 | output.versionCodeOverride = 175 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 176 | } 177 | 178 | } 179 | } 180 | packagingOptions { 181 | pickFirst '**/*.so' 182 | } 183 | } 184 | 185 | dependencies { 186 | implementation fileTree(dir: "libs", include: ["*.jar"]) 187 | //noinspection GradleDynamicVersion 188 | implementation "com.facebook.react:react-native:+" // From node_modules 189 | 190 | 191 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 192 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 193 | exclude group:'com.facebook.fbjni' 194 | } 195 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 196 | exclude group:'com.facebook.flipper' 197 | exclude group:'com.squareup.okhttp3', module:'okhttp' 198 | } 199 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 200 | exclude group:'com.facebook.flipper' 201 | } 202 | 203 | if (enableHermes) { 204 | def hermesPath = "../../node_modules/hermes-engine/android/"; 205 | debugImplementation files(hermesPath + "hermes-debug.aar") 206 | releaseImplementation files(hermesPath + "hermes-release.aar") 207 | } else { 208 | implementation jscFlavor 209 | } 210 | 211 | implementation project(':reactnativemultithreading') 212 | } 213 | 214 | // Run this once to be able to run the application with BUCK 215 | // puts all compile dependencies into folder libs for BUCK to use 216 | task copyDownloadableDepsToLibs(type: Copy) { 217 | from configurations.compile 218 | into 'libs' 219 | } 220 | 221 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 222 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | -keep class com.facebook.react.turbomodule.** { *; } 13 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/example/reactnativemultithreading/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.example.reactnativemultithreading; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 32 | client.addPlugin(new ReactFlipperPlugin()); 33 | client.addPlugin(new DatabasesFlipperPlugin(context)); 34 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 35 | client.addPlugin(CrashReporterPlugin.getInstance()); 36 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 37 | NetworkingModule.setCustomClientBuilder( 38 | new NetworkingModule.CustomClientBuilder() { 39 | @Override 40 | public void apply(OkHttpClient.Builder builder) { 41 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 42 | } 43 | }); 44 | client.addPlugin(networkFlipperPlugin); 45 | client.start(); 46 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 47 | // Hence we run if after all native modules have been initialized 48 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 49 | if (reactContext == null) { 50 | reactInstanceManager.addReactInstanceEventListener( 51 | new ReactInstanceManager.ReactInstanceEventListener() { 52 | @Override 53 | public void onReactContextInitialized(ReactContext reactContext) { 54 | reactInstanceManager.removeReactInstanceEventListener(this); 55 | reactContext.runOnNativeModulesQueueThread( 56 | new Runnable() { 57 | @Override 58 | public void run() { 59 | client.addPlugin(new FrescoFlipperPlugin()); 60 | } 61 | }); 62 | } 63 | }); 64 | } else { 65 | client.addPlugin(new FrescoFlipperPlugin()); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/reactnativemultithreading/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.reactnativemultithreading; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "MultithreadingExample"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/reactnativemultithreading/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.reactnativemultithreading; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.ReactInstanceManager; 10 | import com.facebook.soloader.SoLoader; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.util.List; 13 | 14 | import com.reactnativemultithreading.MultithreadingJSIModulePackage; 15 | import com.reactnativemultithreading.MultithreadingPackage; 16 | 17 | import com.facebook.react.bridge.JSIModulePackage; 18 | 19 | public class MainApplication extends Application implements ReactApplication { 20 | 21 | private final ReactNativeHost mReactNativeHost = 22 | new ReactNativeHost(this) { 23 | @Override 24 | public boolean getUseDeveloperSupport() { 25 | return BuildConfig.DEBUG; 26 | } 27 | 28 | @Override 29 | protected List getPackages() { 30 | @SuppressWarnings("UnnecessaryLocalVariable") 31 | List packages = new PackageList(this).getPackages(); 32 | // Packages that cannot be autolinked yet can be added manually here, for MultithreadingExample: 33 | // packages.add(new MyReactNativePackage()); 34 | packages.add(new MultithreadingPackage()); 35 | return packages; 36 | } 37 | 38 | @Override 39 | protected String getJSMainModuleName() { 40 | return "index"; 41 | } 42 | 43 | @Override 44 | protected JSIModulePackage getJSIModulePackage() { 45 | return new MultithreadingJSIModulePackage(); 46 | } 47 | }; 48 | 49 | @Override 50 | public ReactNativeHost getReactNativeHost() { 51 | return mReactNativeHost; 52 | } 53 | 54 | @Override 55 | public void onCreate() { 56 | super.onCreate(); 57 | SoLoader.init(this, /* native exopackage */ false); 58 | // initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); // Remove this line if you don't want Flipper enabled 59 | } 60 | 61 | /** 62 | * Loads Flipper in React Native templates. 63 | * 64 | * @param context 65 | */ 66 | private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 67 | if (BuildConfig.DEBUG) { 68 | try { 69 | /* 70 | We use reflection here to pick up the class that initializes Flipper, 71 | since Flipper library is not available in release mode 72 | */ 73 | Class aClass = Class.forName("com.reactnativemultithreadingExample.ReactNativeFlipper"); 74 | aClass 75 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 76 | .invoke(null, context, reactInstanceManager); 77 | } catch (ClassNotFoundException e) { 78 | e.printStackTrace(); 79 | } catch (NoSuchMethodException e) { 80 | e.printStackTrace(); 81 | } catch (IllegalAccessException e) { 82 | e.printStackTrace(); 83 | } catch (InvocationTargetException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Multithreading Example 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.3" 6 | minSdkVersion = 21 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | ndkVersion = "20.1.5948944" 10 | } 11 | repositories { 12 | google() 13 | jcenter() 14 | } 15 | dependencies { 16 | classpath('com.android.tools.build:gradle:4.1.3') 17 | 18 | // NOTE: Do not place your application dependencies here; they belong 19 | // in the individual module build.gradle files 20 | } 21 | } 22 | 23 | allprojects { 24 | repositories { 25 | mavenLocal() 26 | maven { 27 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 28 | url("$rootDir/../node_modules/react-native/android") 29 | } 30 | maven { 31 | // Android JSC is installed from npm 32 | url("$rootDir/../node_modules/jsc-android/dist") 33 | } 34 | 35 | google() 36 | jcenter() 37 | maven { url 'https://www.jitpack.io' } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | FLIPPER_VERSION=0.75.1 23 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'MultithreadingExample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | 5 | include ':reactnativemultithreading' 6 | project(':reactnativemultithreading').projectDir = new File(rootProject.projectDir, '../../android') 7 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MultithreadingExample", 3 | "displayName": "Multithreading Example" 4 | } 5 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | presets: ['module:metro-react-native-babel-preset'], 6 | plugins: [ 7 | [ 8 | 'module-resolver', 9 | { 10 | alias: { 11 | [pak.name]: path.join(__dirname, '..', pak.source), 12 | }, 13 | }, 14 | ], 15 | 'react-native-reanimated/plugin', 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /example/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare var _LABEL: string | undefined; 2 | declare var _setGlobalConsole: (console: any) => void; 3 | declare var nativeLoggingHook: (message: string, severity: number) => void; 4 | declare var _log: (message: string) => void; 5 | declare var performance: { 6 | now: () => number; 7 | }; 8 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import 'react-native-gesture-handler'; 2 | import React from 'react'; 3 | import { AppRegistry } from 'react-native'; 4 | import Fibonacci from './src/Fibonacci'; 5 | import ThreadBlocking from './src/ThreadBlocking'; 6 | import { name as appName } from './app.json'; 7 | import { NavigationContainer } from '@react-navigation/native'; 8 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; 9 | 10 | const Tab = createBottomTabNavigator(); 11 | function App() { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | } 21 | 22 | AppRegistry.registerComponent(appName, () => App); 23 | -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // MultithreadingExample 4 | // 5 | 6 | import Foundation 7 | -------------------------------------------------------------------------------- /example/ios/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods' 4 | -------------------------------------------------------------------------------- /example/ios/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.3) 5 | activesupport (5.2.5) 6 | concurrent-ruby (~> 1.0, >= 1.0.2) 7 | i18n (>= 0.7, < 2) 8 | minitest (~> 5.1) 9 | tzinfo (~> 1.1) 10 | addressable (2.7.0) 11 | public_suffix (>= 2.0.2, < 5.0) 12 | algoliasearch (1.27.5) 13 | httpclient (~> 2.8, >= 2.8.3) 14 | json (>= 1.5.1) 15 | atomos (0.1.3) 16 | claide (1.0.3) 17 | cocoapods (1.10.1) 18 | addressable (~> 2.6) 19 | claide (>= 1.0.2, < 2.0) 20 | cocoapods-core (= 1.10.1) 21 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 22 | cocoapods-downloader (>= 1.4.0, < 2.0) 23 | cocoapods-plugins (>= 1.0.0, < 2.0) 24 | cocoapods-search (>= 1.0.0, < 2.0) 25 | cocoapods-trunk (>= 1.4.0, < 2.0) 26 | cocoapods-try (>= 1.1.0, < 2.0) 27 | colored2 (~> 3.1) 28 | escape (~> 0.0.4) 29 | fourflusher (>= 2.3.0, < 3.0) 30 | gh_inspector (~> 1.0) 31 | molinillo (~> 0.6.6) 32 | nap (~> 1.0) 33 | ruby-macho (~> 1.4) 34 | xcodeproj (>= 1.19.0, < 2.0) 35 | cocoapods-core (1.10.1) 36 | activesupport (> 5.0, < 6) 37 | addressable (~> 2.6) 38 | algoliasearch (~> 1.0) 39 | concurrent-ruby (~> 1.1) 40 | fuzzy_match (~> 2.0.4) 41 | nap (~> 1.0) 42 | netrc (~> 0.11) 43 | public_suffix 44 | typhoeus (~> 1.0) 45 | cocoapods-deintegrate (1.0.4) 46 | cocoapods-downloader (1.4.0) 47 | cocoapods-plugins (1.0.0) 48 | nap 49 | cocoapods-search (1.0.0) 50 | cocoapods-trunk (1.5.0) 51 | nap (>= 0.8, < 2.0) 52 | netrc (~> 0.11) 53 | cocoapods-try (1.2.0) 54 | colored2 (3.1.2) 55 | concurrent-ruby (1.1.8) 56 | escape (0.0.4) 57 | ethon (0.13.0) 58 | ffi (>= 1.15.0) 59 | ffi (1.15.0) 60 | fourflusher (2.3.1) 61 | fuzzy_match (2.0.4) 62 | gh_inspector (1.1.3) 63 | httpclient (2.8.3) 64 | i18n (1.8.10) 65 | concurrent-ruby (~> 1.0) 66 | json (2.5.1) 67 | minitest (5.14.4) 68 | molinillo (0.6.6) 69 | nanaimo (0.3.0) 70 | nap (1.1.0) 71 | netrc (0.11.0) 72 | public_suffix (4.0.6) 73 | ruby-macho (1.4.0) 74 | thread_safe (0.3.6) 75 | typhoeus (1.4.0) 76 | ethon (>= 0.9.0) 77 | tzinfo (1.2.9) 78 | thread_safe (~> 0.1) 79 | xcodeproj (1.19.0) 80 | CFPropertyList (>= 2.3.3, < 4.0) 81 | atomos (~> 0.1.3) 82 | claide (>= 1.0.2, < 2.0) 83 | colored2 (~> 3.1) 84 | nanaimo (~> 0.3.0) 85 | 86 | PLATFORMS 87 | ruby 88 | 89 | DEPENDENCIES 90 | cocoapods 91 | 92 | BUNDLED WITH 93 | 2.1.4 94 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample.xcodeproj/xcshareddata/xcschemes/MultithreadingExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 81 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | #ifdef FB_SONARKIT_ENABLED 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | static void InitializeFlipper(UIApplication *application) { 22 | FlipperClient *client = [FlipperClient sharedClient]; 23 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 24 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 25 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 26 | [client addPlugin:[FlipperKitReactPlugin new]]; 27 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 28 | [client start]; 29 | } 30 | #endif 31 | 32 | @implementation AppDelegate 33 | 34 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 35 | { 36 | #ifdef FB_SONARKIT_ENABLED 37 | InitializeFlipper(application); 38 | #endif 39 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 40 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 41 | moduleName:@"MultithreadingExample" 42 | initialProperties:nil]; 43 | 44 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 45 | 46 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 47 | UIViewController *rootViewController = [UIViewController new]; 48 | rootViewController.view = rootView; 49 | self.window.rootViewController = rootViewController; 50 | [self.window makeKeyAndVisible]; 51 | return YES; 52 | } 53 | 54 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 55 | { 56 | #if DEBUG 57 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 58 | #else 59 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 60 | #endif 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/ios/MultithreadingExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Multithreading Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/ios/MultithreadingExample/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'MultithreadingExample' do 7 | config = use_native_modules! 8 | 9 | use_react_native!( 10 | :path => config["reactNativePath"], 11 | :hermes_enabled => true 12 | ) 13 | 14 | pod 'react-native-multithreading', :path => '../..' 15 | 16 | # Enables Flipper. 17 | # 18 | # Note that if you have use_frameworks! enabled, Flipper will not work and 19 | # you should disable these next few lines. 20 | use_flipper!() 21 | 22 | post_install do |installer| 23 | react_native_post_install(installer) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - boost-for-react-native (1.63.0) 3 | - CocoaAsyncSocket (7.6.5) 4 | - DoubleConversion (1.1.6) 5 | - FBLazyVector (0.64.0) 6 | - FBReactNativeSpec (0.64.0): 7 | - RCT-Folly (= 2020.01.13.00) 8 | - RCTRequired (= 0.64.0) 9 | - RCTTypeSafety (= 0.64.0) 10 | - React-Core (= 0.64.0) 11 | - React-jsi (= 0.64.0) 12 | - ReactCommon/turbomodule/core (= 0.64.0) 13 | - Flipper (0.75.1): 14 | - Flipper-Folly (~> 2.5) 15 | - Flipper-RSocket (~> 1.3) 16 | - Flipper-DoubleConversion (1.1.7) 17 | - Flipper-Folly (2.5.1): 18 | - boost-for-react-native 19 | - Flipper-DoubleConversion 20 | - Flipper-Glog 21 | - libevent (~> 2.1.12) 22 | - OpenSSL-Universal (= 1.1.180) 23 | - Flipper-Glog (0.3.6) 24 | - Flipper-PeerTalk (0.0.4) 25 | - Flipper-RSocket (1.3.0): 26 | - Flipper-Folly (~> 2.5) 27 | - FlipperKit (0.75.1): 28 | - FlipperKit/Core (= 0.75.1) 29 | - FlipperKit/Core (0.75.1): 30 | - Flipper (~> 0.75.1) 31 | - FlipperKit/CppBridge 32 | - FlipperKit/FBCxxFollyDynamicConvert 33 | - FlipperKit/FBDefines 34 | - FlipperKit/FKPortForwarding 35 | - FlipperKit/CppBridge (0.75.1): 36 | - Flipper (~> 0.75.1) 37 | - FlipperKit/FBCxxFollyDynamicConvert (0.75.1): 38 | - Flipper-Folly (~> 2.5) 39 | - FlipperKit/FBDefines (0.75.1) 40 | - FlipperKit/FKPortForwarding (0.75.1): 41 | - CocoaAsyncSocket (~> 7.6) 42 | - Flipper-PeerTalk (~> 0.0.4) 43 | - FlipperKit/FlipperKitHighlightOverlay (0.75.1) 44 | - FlipperKit/FlipperKitLayoutPlugin (0.75.1): 45 | - FlipperKit/Core 46 | - FlipperKit/FlipperKitHighlightOverlay 47 | - FlipperKit/FlipperKitLayoutTextSearchable 48 | - YogaKit (~> 1.18) 49 | - FlipperKit/FlipperKitLayoutTextSearchable (0.75.1) 50 | - FlipperKit/FlipperKitNetworkPlugin (0.75.1): 51 | - FlipperKit/Core 52 | - FlipperKit/FlipperKitReactPlugin (0.75.1): 53 | - FlipperKit/Core 54 | - FlipperKit/FlipperKitUserDefaultsPlugin (0.75.1): 55 | - FlipperKit/Core 56 | - FlipperKit/SKIOSNetworkPlugin (0.75.1): 57 | - FlipperKit/Core 58 | - FlipperKit/FlipperKitNetworkPlugin 59 | - glog (0.3.5) 60 | - hermes-engine (0.7.2) 61 | - libevent (2.1.12) 62 | - OpenSSL-Universal (1.1.180) 63 | - RCT-Folly (2020.01.13.00): 64 | - boost-for-react-native 65 | - DoubleConversion 66 | - glog 67 | - RCT-Folly/Default (= 2020.01.13.00) 68 | - RCT-Folly/Default (2020.01.13.00): 69 | - boost-for-react-native 70 | - DoubleConversion 71 | - glog 72 | - RCT-Folly/Futures (2020.01.13.00): 73 | - boost-for-react-native 74 | - DoubleConversion 75 | - glog 76 | - libevent 77 | - RCTRequired (0.64.0) 78 | - RCTTypeSafety (0.64.0): 79 | - FBLazyVector (= 0.64.0) 80 | - RCT-Folly (= 2020.01.13.00) 81 | - RCTRequired (= 0.64.0) 82 | - React-Core (= 0.64.0) 83 | - React (0.64.0): 84 | - React-Core (= 0.64.0) 85 | - React-Core/DevSupport (= 0.64.0) 86 | - React-Core/RCTWebSocket (= 0.64.0) 87 | - React-RCTActionSheet (= 0.64.0) 88 | - React-RCTAnimation (= 0.64.0) 89 | - React-RCTBlob (= 0.64.0) 90 | - React-RCTImage (= 0.64.0) 91 | - React-RCTLinking (= 0.64.0) 92 | - React-RCTNetwork (= 0.64.0) 93 | - React-RCTSettings (= 0.64.0) 94 | - React-RCTText (= 0.64.0) 95 | - React-RCTVibration (= 0.64.0) 96 | - React-callinvoker (0.64.0) 97 | - React-Core (0.64.0): 98 | - glog 99 | - RCT-Folly (= 2020.01.13.00) 100 | - React-Core/Default (= 0.64.0) 101 | - React-cxxreact (= 0.64.0) 102 | - React-jsi (= 0.64.0) 103 | - React-jsiexecutor (= 0.64.0) 104 | - React-perflogger (= 0.64.0) 105 | - Yoga 106 | - React-Core/CoreModulesHeaders (0.64.0): 107 | - glog 108 | - RCT-Folly (= 2020.01.13.00) 109 | - React-Core/Default 110 | - React-cxxreact (= 0.64.0) 111 | - React-jsi (= 0.64.0) 112 | - React-jsiexecutor (= 0.64.0) 113 | - React-perflogger (= 0.64.0) 114 | - Yoga 115 | - React-Core/Default (0.64.0): 116 | - glog 117 | - RCT-Folly (= 2020.01.13.00) 118 | - React-cxxreact (= 0.64.0) 119 | - React-jsi (= 0.64.0) 120 | - React-jsiexecutor (= 0.64.0) 121 | - React-perflogger (= 0.64.0) 122 | - Yoga 123 | - React-Core/DevSupport (0.64.0): 124 | - glog 125 | - RCT-Folly (= 2020.01.13.00) 126 | - React-Core/Default (= 0.64.0) 127 | - React-Core/RCTWebSocket (= 0.64.0) 128 | - React-cxxreact (= 0.64.0) 129 | - React-jsi (= 0.64.0) 130 | - React-jsiexecutor (= 0.64.0) 131 | - React-jsinspector (= 0.64.0) 132 | - React-perflogger (= 0.64.0) 133 | - Yoga 134 | - React-Core/Hermes (0.64.0): 135 | - glog 136 | - hermes-engine 137 | - RCT-Folly (= 2020.01.13.00) 138 | - RCT-Folly/Futures 139 | - React-cxxreact (= 0.64.0) 140 | - React-jsi (= 0.64.0) 141 | - React-jsiexecutor (= 0.64.0) 142 | - React-perflogger (= 0.64.0) 143 | - Yoga 144 | - React-Core/RCTActionSheetHeaders (0.64.0): 145 | - glog 146 | - RCT-Folly (= 2020.01.13.00) 147 | - React-Core/Default 148 | - React-cxxreact (= 0.64.0) 149 | - React-jsi (= 0.64.0) 150 | - React-jsiexecutor (= 0.64.0) 151 | - React-perflogger (= 0.64.0) 152 | - Yoga 153 | - React-Core/RCTAnimationHeaders (0.64.0): 154 | - glog 155 | - RCT-Folly (= 2020.01.13.00) 156 | - React-Core/Default 157 | - React-cxxreact (= 0.64.0) 158 | - React-jsi (= 0.64.0) 159 | - React-jsiexecutor (= 0.64.0) 160 | - React-perflogger (= 0.64.0) 161 | - Yoga 162 | - React-Core/RCTBlobHeaders (0.64.0): 163 | - glog 164 | - RCT-Folly (= 2020.01.13.00) 165 | - React-Core/Default 166 | - React-cxxreact (= 0.64.0) 167 | - React-jsi (= 0.64.0) 168 | - React-jsiexecutor (= 0.64.0) 169 | - React-perflogger (= 0.64.0) 170 | - Yoga 171 | - React-Core/RCTImageHeaders (0.64.0): 172 | - glog 173 | - RCT-Folly (= 2020.01.13.00) 174 | - React-Core/Default 175 | - React-cxxreact (= 0.64.0) 176 | - React-jsi (= 0.64.0) 177 | - React-jsiexecutor (= 0.64.0) 178 | - React-perflogger (= 0.64.0) 179 | - Yoga 180 | - React-Core/RCTLinkingHeaders (0.64.0): 181 | - glog 182 | - RCT-Folly (= 2020.01.13.00) 183 | - React-Core/Default 184 | - React-cxxreact (= 0.64.0) 185 | - React-jsi (= 0.64.0) 186 | - React-jsiexecutor (= 0.64.0) 187 | - React-perflogger (= 0.64.0) 188 | - Yoga 189 | - React-Core/RCTNetworkHeaders (0.64.0): 190 | - glog 191 | - RCT-Folly (= 2020.01.13.00) 192 | - React-Core/Default 193 | - React-cxxreact (= 0.64.0) 194 | - React-jsi (= 0.64.0) 195 | - React-jsiexecutor (= 0.64.0) 196 | - React-perflogger (= 0.64.0) 197 | - Yoga 198 | - React-Core/RCTSettingsHeaders (0.64.0): 199 | - glog 200 | - RCT-Folly (= 2020.01.13.00) 201 | - React-Core/Default 202 | - React-cxxreact (= 0.64.0) 203 | - React-jsi (= 0.64.0) 204 | - React-jsiexecutor (= 0.64.0) 205 | - React-perflogger (= 0.64.0) 206 | - Yoga 207 | - React-Core/RCTTextHeaders (0.64.0): 208 | - glog 209 | - RCT-Folly (= 2020.01.13.00) 210 | - React-Core/Default 211 | - React-cxxreact (= 0.64.0) 212 | - React-jsi (= 0.64.0) 213 | - React-jsiexecutor (= 0.64.0) 214 | - React-perflogger (= 0.64.0) 215 | - Yoga 216 | - React-Core/RCTVibrationHeaders (0.64.0): 217 | - glog 218 | - RCT-Folly (= 2020.01.13.00) 219 | - React-Core/Default 220 | - React-cxxreact (= 0.64.0) 221 | - React-jsi (= 0.64.0) 222 | - React-jsiexecutor (= 0.64.0) 223 | - React-perflogger (= 0.64.0) 224 | - Yoga 225 | - React-Core/RCTWebSocket (0.64.0): 226 | - glog 227 | - RCT-Folly (= 2020.01.13.00) 228 | - React-Core/Default (= 0.64.0) 229 | - React-cxxreact (= 0.64.0) 230 | - React-jsi (= 0.64.0) 231 | - React-jsiexecutor (= 0.64.0) 232 | - React-perflogger (= 0.64.0) 233 | - Yoga 234 | - React-CoreModules (0.64.0): 235 | - FBReactNativeSpec (= 0.64.0) 236 | - RCT-Folly (= 2020.01.13.00) 237 | - RCTTypeSafety (= 0.64.0) 238 | - React-Core/CoreModulesHeaders (= 0.64.0) 239 | - React-jsi (= 0.64.0) 240 | - React-RCTImage (= 0.64.0) 241 | - ReactCommon/turbomodule/core (= 0.64.0) 242 | - React-cxxreact (0.64.0): 243 | - boost-for-react-native (= 1.63.0) 244 | - DoubleConversion 245 | - glog 246 | - RCT-Folly (= 2020.01.13.00) 247 | - React-callinvoker (= 0.64.0) 248 | - React-jsi (= 0.64.0) 249 | - React-jsinspector (= 0.64.0) 250 | - React-perflogger (= 0.64.0) 251 | - React-runtimeexecutor (= 0.64.0) 252 | - React-jsi (0.64.0): 253 | - boost-for-react-native (= 1.63.0) 254 | - DoubleConversion 255 | - glog 256 | - RCT-Folly (= 2020.01.13.00) 257 | - React-jsi/Default (= 0.64.0) 258 | - React-jsi/Default (0.64.0): 259 | - boost-for-react-native (= 1.63.0) 260 | - DoubleConversion 261 | - glog 262 | - RCT-Folly (= 2020.01.13.00) 263 | - React-jsiexecutor (0.64.0): 264 | - DoubleConversion 265 | - glog 266 | - RCT-Folly (= 2020.01.13.00) 267 | - React-cxxreact (= 0.64.0) 268 | - React-jsi (= 0.64.0) 269 | - React-perflogger (= 0.64.0) 270 | - React-jsinspector (0.64.0) 271 | - react-native-multithreading (1.0.0): 272 | - React 273 | - React-callinvoker 274 | - React-Core 275 | - react-native-safe-area-context (3.2.0): 276 | - React-Core 277 | - React-perflogger (0.64.0) 278 | - React-RCTActionSheet (0.64.0): 279 | - React-Core/RCTActionSheetHeaders (= 0.64.0) 280 | - React-RCTAnimation (0.64.0): 281 | - FBReactNativeSpec (= 0.64.0) 282 | - RCT-Folly (= 2020.01.13.00) 283 | - RCTTypeSafety (= 0.64.0) 284 | - React-Core/RCTAnimationHeaders (= 0.64.0) 285 | - React-jsi (= 0.64.0) 286 | - ReactCommon/turbomodule/core (= 0.64.0) 287 | - React-RCTBlob (0.64.0): 288 | - FBReactNativeSpec (= 0.64.0) 289 | - RCT-Folly (= 2020.01.13.00) 290 | - React-Core/RCTBlobHeaders (= 0.64.0) 291 | - React-Core/RCTWebSocket (= 0.64.0) 292 | - React-jsi (= 0.64.0) 293 | - React-RCTNetwork (= 0.64.0) 294 | - ReactCommon/turbomodule/core (= 0.64.0) 295 | - React-RCTImage (0.64.0): 296 | - FBReactNativeSpec (= 0.64.0) 297 | - RCT-Folly (= 2020.01.13.00) 298 | - RCTTypeSafety (= 0.64.0) 299 | - React-Core/RCTImageHeaders (= 0.64.0) 300 | - React-jsi (= 0.64.0) 301 | - React-RCTNetwork (= 0.64.0) 302 | - ReactCommon/turbomodule/core (= 0.64.0) 303 | - React-RCTLinking (0.64.0): 304 | - FBReactNativeSpec (= 0.64.0) 305 | - React-Core/RCTLinkingHeaders (= 0.64.0) 306 | - React-jsi (= 0.64.0) 307 | - ReactCommon/turbomodule/core (= 0.64.0) 308 | - React-RCTNetwork (0.64.0): 309 | - FBReactNativeSpec (= 0.64.0) 310 | - RCT-Folly (= 2020.01.13.00) 311 | - RCTTypeSafety (= 0.64.0) 312 | - React-Core/RCTNetworkHeaders (= 0.64.0) 313 | - React-jsi (= 0.64.0) 314 | - ReactCommon/turbomodule/core (= 0.64.0) 315 | - React-RCTSettings (0.64.0): 316 | - FBReactNativeSpec (= 0.64.0) 317 | - RCT-Folly (= 2020.01.13.00) 318 | - RCTTypeSafety (= 0.64.0) 319 | - React-Core/RCTSettingsHeaders (= 0.64.0) 320 | - React-jsi (= 0.64.0) 321 | - ReactCommon/turbomodule/core (= 0.64.0) 322 | - React-RCTText (0.64.0): 323 | - React-Core/RCTTextHeaders (= 0.64.0) 324 | - React-RCTVibration (0.64.0): 325 | - FBReactNativeSpec (= 0.64.0) 326 | - RCT-Folly (= 2020.01.13.00) 327 | - React-Core/RCTVibrationHeaders (= 0.64.0) 328 | - React-jsi (= 0.64.0) 329 | - ReactCommon/turbomodule/core (= 0.64.0) 330 | - React-runtimeexecutor (0.64.0): 331 | - React-jsi (= 0.64.0) 332 | - ReactCommon/turbomodule/core (0.64.0): 333 | - DoubleConversion 334 | - glog 335 | - RCT-Folly (= 2020.01.13.00) 336 | - React-callinvoker (= 0.64.0) 337 | - React-Core (= 0.64.0) 338 | - React-cxxreact (= 0.64.0) 339 | - React-jsi (= 0.64.0) 340 | - React-perflogger (= 0.64.0) 341 | - RNCMaskedView (0.1.10): 342 | - React 343 | - RNGestureHandler (1.10.3): 344 | - React-Core 345 | - RNReanimated (2.1.0): 346 | - DoubleConversion 347 | - FBLazyVector 348 | - FBReactNativeSpec 349 | - glog 350 | - RCT-Folly 351 | - RCTRequired 352 | - RCTTypeSafety 353 | - React 354 | - React-callinvoker 355 | - React-Core 356 | - React-Core/DevSupport 357 | - React-Core/RCTWebSocket 358 | - React-CoreModules 359 | - React-cxxreact 360 | - React-jsi 361 | - React-jsiexecutor 362 | - React-jsinspector 363 | - React-RCTActionSheet 364 | - React-RCTAnimation 365 | - React-RCTBlob 366 | - React-RCTImage 367 | - React-RCTLinking 368 | - React-RCTNetwork 369 | - React-RCTSettings 370 | - React-RCTText 371 | - React-RCTVibration 372 | - ReactCommon/turbomodule/core 373 | - Yoga 374 | - RNScreens (3.0.0): 375 | - React-Core 376 | - Yoga (1.14.0) 377 | - YogaKit (1.18.1): 378 | - Yoga (~> 1.14) 379 | 380 | DEPENDENCIES: 381 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) 382 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) 383 | - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) 384 | - Flipper (~> 0.75.1) 385 | - Flipper-DoubleConversion (= 1.1.7) 386 | - Flipper-Folly (~> 2.5) 387 | - Flipper-Glog (= 0.3.6) 388 | - Flipper-PeerTalk (~> 0.0.4) 389 | - Flipper-RSocket (~> 1.3) 390 | - FlipperKit (~> 0.75.1) 391 | - FlipperKit/Core (~> 0.75.1) 392 | - FlipperKit/CppBridge (~> 0.75.1) 393 | - FlipperKit/FBCxxFollyDynamicConvert (~> 0.75.1) 394 | - FlipperKit/FBDefines (~> 0.75.1) 395 | - FlipperKit/FKPortForwarding (~> 0.75.1) 396 | - FlipperKit/FlipperKitHighlightOverlay (~> 0.75.1) 397 | - FlipperKit/FlipperKitLayoutPlugin (~> 0.75.1) 398 | - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.75.1) 399 | - FlipperKit/FlipperKitNetworkPlugin (~> 0.75.1) 400 | - FlipperKit/FlipperKitReactPlugin (~> 0.75.1) 401 | - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.75.1) 402 | - FlipperKit/SKIOSNetworkPlugin (~> 0.75.1) 403 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) 404 | - hermes-engine (~> 0.7.2) 405 | - libevent (~> 2.1.12) 406 | - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) 407 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) 408 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) 409 | - React (from `../node_modules/react-native/`) 410 | - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) 411 | - React-Core (from `../node_modules/react-native/`) 412 | - React-Core/DevSupport (from `../node_modules/react-native/`) 413 | - React-Core/Hermes (from `../node_modules/react-native/`) 414 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`) 415 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) 416 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) 417 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) 418 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) 419 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) 420 | - react-native-multithreading (from `../..`) 421 | - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) 422 | - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) 423 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) 424 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) 425 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) 426 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) 427 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) 428 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) 429 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) 430 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`) 431 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) 432 | - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) 433 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) 434 | - "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)" 435 | - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) 436 | - RNReanimated (from `../node_modules/react-native-reanimated`) 437 | - RNScreens (from `../node_modules/react-native-screens`) 438 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) 439 | 440 | SPEC REPOS: 441 | trunk: 442 | - boost-for-react-native 443 | - CocoaAsyncSocket 444 | - Flipper 445 | - Flipper-DoubleConversion 446 | - Flipper-Folly 447 | - Flipper-Glog 448 | - Flipper-PeerTalk 449 | - Flipper-RSocket 450 | - FlipperKit 451 | - hermes-engine 452 | - libevent 453 | - OpenSSL-Universal 454 | - YogaKit 455 | 456 | EXTERNAL SOURCES: 457 | DoubleConversion: 458 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" 459 | FBLazyVector: 460 | :path: "../node_modules/react-native/Libraries/FBLazyVector" 461 | FBReactNativeSpec: 462 | :path: "../node_modules/react-native/React/FBReactNativeSpec" 463 | glog: 464 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" 465 | RCT-Folly: 466 | :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" 467 | RCTRequired: 468 | :path: "../node_modules/react-native/Libraries/RCTRequired" 469 | RCTTypeSafety: 470 | :path: "../node_modules/react-native/Libraries/TypeSafety" 471 | React: 472 | :path: "../node_modules/react-native/" 473 | React-callinvoker: 474 | :path: "../node_modules/react-native/ReactCommon/callinvoker" 475 | React-Core: 476 | :path: "../node_modules/react-native/" 477 | React-CoreModules: 478 | :path: "../node_modules/react-native/React/CoreModules" 479 | React-cxxreact: 480 | :path: "../node_modules/react-native/ReactCommon/cxxreact" 481 | React-jsi: 482 | :path: "../node_modules/react-native/ReactCommon/jsi" 483 | React-jsiexecutor: 484 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor" 485 | React-jsinspector: 486 | :path: "../node_modules/react-native/ReactCommon/jsinspector" 487 | react-native-multithreading: 488 | :path: "../.." 489 | react-native-safe-area-context: 490 | :path: "../node_modules/react-native-safe-area-context" 491 | React-perflogger: 492 | :path: "../node_modules/react-native/ReactCommon/reactperflogger" 493 | React-RCTActionSheet: 494 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS" 495 | React-RCTAnimation: 496 | :path: "../node_modules/react-native/Libraries/NativeAnimation" 497 | React-RCTBlob: 498 | :path: "../node_modules/react-native/Libraries/Blob" 499 | React-RCTImage: 500 | :path: "../node_modules/react-native/Libraries/Image" 501 | React-RCTLinking: 502 | :path: "../node_modules/react-native/Libraries/LinkingIOS" 503 | React-RCTNetwork: 504 | :path: "../node_modules/react-native/Libraries/Network" 505 | React-RCTSettings: 506 | :path: "../node_modules/react-native/Libraries/Settings" 507 | React-RCTText: 508 | :path: "../node_modules/react-native/Libraries/Text" 509 | React-RCTVibration: 510 | :path: "../node_modules/react-native/Libraries/Vibration" 511 | React-runtimeexecutor: 512 | :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" 513 | ReactCommon: 514 | :path: "../node_modules/react-native/ReactCommon" 515 | RNCMaskedView: 516 | :path: "../node_modules/@react-native-community/masked-view" 517 | RNGestureHandler: 518 | :path: "../node_modules/react-native-gesture-handler" 519 | RNReanimated: 520 | :path: "../node_modules/react-native-reanimated" 521 | RNScreens: 522 | :path: "../node_modules/react-native-screens" 523 | Yoga: 524 | :path: "../node_modules/react-native/ReactCommon/yoga" 525 | 526 | SPEC CHECKSUMS: 527 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c 528 | CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 529 | DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de 530 | FBLazyVector: 49cbe4b43e445b06bf29199b6ad2057649e4c8f5 531 | FBReactNativeSpec: fb233f7fcd6846ca325bc49734a0251202645bc7 532 | Flipper: d3da1aa199aad94455ae725e9f3aa43f3ec17021 533 | Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 534 | Flipper-Folly: f7a3caafbd74bda4827954fd7a6e000e36355489 535 | Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 536 | Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 537 | Flipper-RSocket: 602921fee03edacf18f5d6f3d3594ba477f456e5 538 | FlipperKit: 8a20b5c5fcf9436cac58551dc049867247f64b00 539 | glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62 540 | hermes-engine: 7d97ba46a1e29bacf3e3c61ecb2804a5ddd02d4f 541 | libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 542 | OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b 543 | RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c 544 | RCTRequired: 2f8cb5b7533219bf4218a045f92768129cf7050a 545 | RCTTypeSafety: 512728b73549e72ad7330b92f3d42936f2a4de5b 546 | React: 98eac01574128a790f0bbbafe2d1a8607291ac24 547 | React-callinvoker: def3f7fae16192df68d9b69fd4bbb59092ee36bc 548 | React-Core: 70a52aa5dbe9b83befae82038451a7df9fd54c5a 549 | React-CoreModules: 052edef46117862e2570eb3a0f06d81c61d2c4b8 550 | React-cxxreact: c1dc71b30653cfb4770efdafcbdc0ad6d388baab 551 | React-jsi: 74341196d9547cbcbcfa4b3bbbf03af56431d5a1 552 | React-jsiexecutor: 06a9c77b56902ae7ffcdd7a4905f664adc5d237b 553 | React-jsinspector: 0ae35a37b20d5e031eb020a69cc5afdbd6406301 554 | react-native-multithreading: 348efe9b54d1af9f96d3ac1dd65435411aff3cc1 555 | react-native-safe-area-context: f0906bf8bc9835ac9a9d3f97e8bde2a997d8da79 556 | React-perflogger: 9c547d8f06b9bf00cb447f2b75e8d7f19b7e02af 557 | React-RCTActionSheet: 3080b6e12e0e1a5b313c8c0050699b5c794a1b11 558 | React-RCTAnimation: 3f96f21a497ae7dabf4d2f150ee43f906aaf516f 559 | React-RCTBlob: 283b8e5025e7f954176bc48164f846909002f3ed 560 | React-RCTImage: 5088a484faac78f2d877e1b79125d3bb1ea94a16 561 | React-RCTLinking: 5e8fbb3e9a8bc2e4e3eb15b1eb8bda5fcac27b8c 562 | React-RCTNetwork: 38ec277217b1e841d5e6a1fa78da65b9212ccb28 563 | React-RCTSettings: 242d6e692108c3de4f3bb74b7586a8799e9ab070 564 | React-RCTText: 8746736ac8eb5a4a74719aa695b7a236a93a83d2 565 | React-RCTVibration: 0fd6b21751a33cb72fce1a4a33ab9678416d307a 566 | React-runtimeexecutor: cad74a1eaa53ee6e7a3620231939d8fe2c6afcf0 567 | ReactCommon: cfe2b7fd20e0dbd2d1185cd7d8f99633fbc5ff05 568 | RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f 569 | RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211 570 | RNReanimated: b8c8004b43446e3c2709fe64b2b41072f87428ad 571 | RNScreens: e8e8dd0588b5da0ab57dcca76ab9b2d8987757e0 572 | Yoga: 8c8436d4171c87504c648ae23b1d81242bdf3bbf 573 | YogaKit: f782866e155069a2cca2517aafea43200b01fd5a 574 | 575 | PODFILE CHECKSUM: 9dd9d7f276e1d2ba889491214c1b17f53a08742f 576 | 577 | COCOAPODS: 1.10.1 578 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const blacklist = require('metro-config/src/defaults/exclusionList'); 3 | const escape = require('escape-string-regexp'); 4 | const pak = require('../package.json'); 5 | 6 | const root = path.resolve(__dirname, '..'); 7 | 8 | const modules = Object.keys({ 9 | ...pak.peerDependencies, 10 | }); 11 | 12 | module.exports = { 13 | projectRoot: __dirname, 14 | watchFolders: [root], 15 | 16 | // We need to make sure that only one version is loaded for peerDependencies 17 | // So we blacklist them at the root, and alias them to the versions in example's node_modules 18 | resolver: { 19 | blacklistRE: blacklist( 20 | modules.map( 21 | (m) => 22 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 23 | ) 24 | ), 25 | 26 | extraNodeModules: modules.reduce((acc, name) => { 27 | acc[name] = path.join(__dirname, 'node_modules', name); 28 | return acc; 29 | }, {}), 30 | }, 31 | 32 | transformer: { 33 | getTransformOptions: async () => ({ 34 | transform: { 35 | experimentalImportSupport: false, 36 | inlineRequires: true, 37 | }, 38 | }), 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-multithreading-example", 3 | "description": "Example app for react-native-multithreading", 4 | "version": "0.0.1", 5 | "private": true, 6 | "scripts": { 7 | "android": "react-native run-android", 8 | "ios": "react-native run-ios", 9 | "start": "react-native start", 10 | "postinstall": "npx patch-package" 11 | }, 12 | "dependencies": { 13 | "@react-native-community/masked-view": "^0.1.10", 14 | "@react-navigation/bottom-tabs": "^5.11.8", 15 | "@react-navigation/native": "^5.9.3", 16 | "babel-plugin-module-resolver": "^4.1.0", 17 | "react": "17.0.1", 18 | "react-native": "0.64.0", 19 | "react-native-gesture-handler": "^1.10.3", 20 | "react-native-reanimated": "^2.1.0", 21 | "react-native-safe-area-context": "^3.2.0", 22 | "react-native-screens": "^3.0.0" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.12.9", 26 | "@babel/runtime": "^7.12.5", 27 | "@react-native-community/eslint-config": "^2.0.0", 28 | "babel-jest": "^26.6.3", 29 | "eslint": "7.14.0", 30 | "jest": "^26.6.3", 31 | "metro-react-native-babel-preset": "^0.64.0", 32 | "patch-package": "^6.4.7", 33 | "react-test-renderer": "17.0.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/src/Fibonacci.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { 4 | StyleSheet, 5 | View, 6 | Text, 7 | TextInput, 8 | ActivityIndicator, 9 | Alert, 10 | } from 'react-native'; 11 | import { spawnThread } from 'react-native-multithreading'; 12 | import 'react-native-reanimated'; 13 | 14 | // calculates the fibonacci number - that can be optimized really good so it's really really fast. 15 | const fibonacci = (num: number): number => { 16 | 'worklet'; 17 | // Uses array to store every single fibonacci number 18 | var i; 19 | let fib: number[] = []; 20 | 21 | fib[0] = 0; 22 | fib[1] = 1; 23 | for (i = 2; i <= num; i++) { 24 | fib[i] = fib[i - 2] + fib[i - 1]; 25 | } 26 | return fib[fib.length - 1]; 27 | }; 28 | 29 | export default function App() { 30 | const [isRunning, setIsRunning] = React.useState(false); 31 | const [input, setInput] = React.useState('5'); 32 | const [result, setResult] = React.useState(); 33 | 34 | const runFibonacci = React.useCallback(async (parsedInput: number) => { 35 | setIsRunning(true); 36 | try { 37 | const fib = await spawnThread(() => { 38 | 'worklet'; 39 | console.log( 40 | `${global._LABEL}: Calculating fibonacci for input ${parsedInput}...` 41 | ); 42 | const value = fibonacci(parsedInput); 43 | console.log( 44 | `${global._LABEL}: Fibonacci number for ${parsedInput} is ${value}!` 45 | ); 46 | return value; 47 | }); 48 | setResult(fib); 49 | } catch (e) { 50 | const msg = e instanceof Error ? e.message : JSON.stringify(e); 51 | Alert.alert('Error', msg); 52 | } finally { 53 | setIsRunning(false); 54 | } 55 | }, []); 56 | 57 | React.useEffect(() => { 58 | const parsedInput = Number.parseInt(input, 10); 59 | runFibonacci(parsedInput); 60 | }, [runFibonacci, input]); 61 | 62 | return ( 63 | 64 | 65 | In this example you can enter a number in the TextInput while the custom 66 | thread will calculate the fibonacci sequence for the given number 67 | completely async and in parallel, while the React-JS Thread stays fully 68 | responsive. 69 | 70 | Input: 71 | 77 | {isRunning ? ( 78 | 79 | ) : ( 80 | Fibonacci Number: {result} 81 | )} 82 | 83 | ); 84 | } 85 | 86 | const styles = StyleSheet.create({ 87 | container: { 88 | flex: 1, 89 | alignItems: 'center', 90 | paddingTop: 70, 91 | }, 92 | description: { 93 | maxWidth: '80%', 94 | fontSize: 15, 95 | color: '#242424', 96 | marginBottom: 80, 97 | }, 98 | input: { 99 | width: '50%', 100 | paddingVertical: 5, 101 | marginVertical: 10, 102 | borderWidth: StyleSheet.hairlineWidth, 103 | borderRadius: 5, 104 | borderColor: 'black', 105 | textAlign: 'center', 106 | fontSize: 14, 107 | }, 108 | }); 109 | -------------------------------------------------------------------------------- /example/src/PressableOpacity.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react'; 2 | import { PressableProps, Pressable, ViewStyle } from 'react-native'; 3 | 4 | export interface PressableOpacityProps extends PressableProps { 5 | disabledOpacity?: number; 6 | activeOpacity?: number; 7 | } 8 | 9 | export const PressableOpacity = ({ 10 | style, 11 | disabled = false, 12 | disabledOpacity = 1, 13 | activeOpacity = 0.2, 14 | ...passThroughProps 15 | }: PressableOpacityProps): React.ReactElement => { 16 | const getOpacity = useCallback( 17 | (pressed: boolean) => { 18 | if (disabled) { 19 | return disabledOpacity; 20 | } else { 21 | if (pressed) return activeOpacity; 22 | else return 1; 23 | } 24 | }, 25 | [activeOpacity, disabled, disabledOpacity] 26 | ); 27 | const _style = useCallback( 28 | ({ pressed }) => [style as ViewStyle, { opacity: getOpacity(pressed) }], 29 | [getOpacity, style] 30 | ); 31 | 32 | return ; 33 | }; 34 | -------------------------------------------------------------------------------- /example/src/ThreadBlocking.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { StyleSheet, View, Text } from 'react-native'; 4 | import { spawnThread } from 'react-native-multithreading'; 5 | import 'react-native-reanimated'; 6 | import { PressableOpacity } from './PressableOpacity'; 7 | 8 | // complex branches with modular if so that it cannot be optimized very well. Takes ~4-5 seconds on my i9. 9 | const benchmark = () => { 10 | 'worklet'; 11 | // global.performance is not yet installed. I will do that soon. 12 | const start = performance.now(); 13 | function p(n: number) { 14 | for (var i = 2; i * i <= n; i++) { 15 | if (n % i === 0) { 16 | return false; 17 | } 18 | } 19 | return true; 20 | } 21 | 22 | var sum = 0; 23 | for (var k = 2; k < 1000000; k++) { 24 | if (p(k)) { 25 | sum++; 26 | } 27 | } 28 | const end = performance.now(); 29 | return { 30 | result: sum, 31 | duration: end - start, 32 | }; 33 | }; 34 | 35 | const BENCHMARK_TIMES = 5; 36 | 37 | export default function ThreadBlocking() { 38 | const [isBlockingCustomThread, setIsBenchmarkingCustom] = React.useState( 39 | false 40 | ); 41 | const [isBlockingReactThread, setIsBenchmarkingReact] = React.useState(false); 42 | 43 | const runBenchmark = React.useCallback( 44 | async (thread: 'react' | 'custom' | 'ui') => { 45 | console.log( 46 | 'Starting Benchmark - Please see native console (Xcode Logs/Android Logcat) for output!' 47 | ); 48 | switch (thread) { 49 | case 'react': { 50 | setIsBenchmarkingReact(true); 51 | requestAnimationFrame(() => { 52 | global.nativeLoggingHook( 53 | `REACT_THREAD: Begin blocking React-JS Thread...`, 54 | 1 55 | ); 56 | for (let i = 0; i < BENCHMARK_TIMES; i++) { 57 | const r = benchmark(); 58 | global.nativeLoggingHook( 59 | `REACT_THREAD: Run #${i}: ${r.result} (took ${r.duration}ms)`, 60 | 1 61 | ); 62 | } 63 | global.nativeLoggingHook( 64 | `REACT_THREAD: React-JS Thread unblocked!`, 65 | 1 66 | ); 67 | setIsBenchmarkingReact(false); 68 | }); 69 | break; 70 | } 71 | case 'custom': { 72 | setIsBenchmarkingCustom(true); 73 | await spawnThread(() => { 74 | 'worklet'; 75 | _log(`CUSTOM_THREAD: Begin blocking Custom Thread...`); 76 | for (let i = 0; i < BENCHMARK_TIMES; i++) { 77 | const r = benchmark(); 78 | // can't use console.log because that just dispatches to React-JS thread, which might be blocked. 79 | _log( 80 | `CUSTOM_THREAD: Run #${i}: ${r.result} (took ${r.duration}ms)` 81 | ); 82 | } 83 | _log(`CUSTOM_THREAD: Custom Thread unblocked!`); 84 | }); 85 | setIsBenchmarkingCustom(false); 86 | break; 87 | } 88 | } 89 | }, 90 | [] 91 | ); 92 | 93 | return ( 94 | 95 | 96 | In this example you can run a heavy benchmark/calculation on the default 97 | React-JS Thread and on the separate JS Thread. When the default React-JS 98 | Thread gets blocked, your entire application freezes - whereas the 99 | separate Thread does not affect application execution at all and is 100 | perfectly designed for heavy synchronous calculations. 101 | 102 | console.log("REACT_THREAD: Hello, I'm alive 👋")} 105 | > 106 | 107 | Check if React-JS Thread is responsive 108 | 109 | 110 | runBenchmark('react')} 113 | > 114 | 115 | Run heavy calculation on React-JS Thread 116 | 117 | 118 | 119 | React-JS Thread blocked: {isBlockingReactThread ? 'true' : 'false'} 120 | 121 | runBenchmark('custom')} 124 | > 125 | 126 | Run heavy calculation on separate Thread 127 | 128 | 129 | 130 | Custom Thread blocked: {isBlockingCustomThread ? 'true' : 'false'} 131 | 132 | 133 | ); 134 | } 135 | 136 | const styles = StyleSheet.create({ 137 | container: { 138 | flex: 1, 139 | alignItems: 'center', 140 | justifyContent: 'center', 141 | paddingTop: 50, 142 | }, 143 | description: { 144 | maxWidth: '80%', 145 | fontSize: 15, 146 | color: '#242424', 147 | }, 148 | isBlockedText: { 149 | marginTop: 5, 150 | fontSize: 14, 151 | color: '#454545', 152 | }, 153 | aliveButton: { 154 | marginTop: 10, 155 | marginBottom: 40, 156 | backgroundColor: '#90ee90', 157 | borderRadius: 10, 158 | paddingHorizontal: 15, 159 | paddingVertical: 10, 160 | maxWidth: '80%', 161 | }, 162 | button: { 163 | marginTop: 10, 164 | backgroundColor: '#b35f5f', 165 | borderRadius: 10, 166 | paddingHorizontal: 15, 167 | paddingVertical: 10, 168 | }, 169 | buttonText: { 170 | color: 'white', 171 | fontSize: 15, 172 | fontWeight: 'bold', 173 | }, 174 | }); 175 | -------------------------------------------------------------------------------- /img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrousavy/react-native-multithreading/852e60596823c679b92d7dba94f2ec8d83dd912d/img/icon.png -------------------------------------------------------------------------------- /ios/Multithreading.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5E555C0D2413F4C50049A1A2 /* RNMultithreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNMultithreading.mm */; }; 11 | B87D0C2F26174EC7004B892B /* MakeJSIRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B87D0C2E26174EC7004B892B /* MakeJSIRuntime.cpp */; }; 12 | B887B6332600A8E100C68EE7 /* ThreadPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B887B6322600A8E100C68EE7 /* ThreadPool.cpp */; }; 13 | B893C01225FFE54800F249DD /* RNMultithreadingInstaller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B893C01125FFE54800F249DD /* RNMultithreadingInstaller.cpp */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = "include/$(PRODUCT_NAME)"; 21 | dstSubfolderSpec = 16; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 0; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 134814201AA4EA6300B7C361 /* libMultithreading.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMultithreading.a; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | B3E7B5891CC2AC0600A0062D /* RNMultithreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RNMultithreading.mm; sourceTree = ""; }; 31 | B87D0BF726174A7F004B892B /* MakeJSIRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MakeJSIRuntime.h; path = ../cpp/MakeJSIRuntime.h; sourceTree = ""; }; 32 | B87D0C2E26174EC7004B892B /* MakeJSIRuntime.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MakeJSIRuntime.cpp; path = ../cpp/MakeJSIRuntime.cpp; sourceTree = ""; }; 33 | B887B6312600A8CF00C68EE7 /* ThreadPool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadPool.h; path = ../cpp/ThreadPool.h; sourceTree = ""; }; 34 | B887B6322600A8E100C68EE7 /* ThreadPool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPool.cpp; path = ../cpp/ThreadPool.cpp; sourceTree = ""; }; 35 | B893C01025FFE54800F249DD /* RNMultithreadingInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNMultithreadingInstaller.h; path = ../cpp/RNMultithreadingInstaller.h; sourceTree = ""; }; 36 | B893C01125FFE54800F249DD /* RNMultithreadingInstaller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RNMultithreadingInstaller.cpp; path = ../cpp/RNMultithreadingInstaller.cpp; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 134814211AA4EA7D00B7C361 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 134814201AA4EA6300B7C361 /* libMultithreading.a */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 58B511D21A9E6C8500147676 = { 59 | isa = PBXGroup; 60 | children = ( 61 | B87D0BF726174A7F004B892B /* MakeJSIRuntime.h */, 62 | B87D0C2E26174EC7004B892B /* MakeJSIRuntime.cpp */, 63 | B887B6312600A8CF00C68EE7 /* ThreadPool.h */, 64 | B887B6322600A8E100C68EE7 /* ThreadPool.cpp */, 65 | B893C01125FFE54800F249DD /* RNMultithreadingInstaller.cpp */, 66 | B893C01025FFE54800F249DD /* RNMultithreadingInstaller.h */, 67 | B3E7B5891CC2AC0600A0062D /* RNMultithreading.mm */, 68 | 134814211AA4EA7D00B7C361 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | /* End PBXGroup section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | 58B511DA1A9E6C8500147676 /* Multithreading */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "Multithreading" */; 78 | buildPhases = ( 79 | 58B511D71A9E6C8500147676 /* Sources */, 80 | 58B511D81A9E6C8500147676 /* Frameworks */, 81 | 58B511D91A9E6C8500147676 /* CopyFiles */, 82 | ); 83 | buildRules = ( 84 | ); 85 | dependencies = ( 86 | ); 87 | name = Multithreading; 88 | productName = RCTDataManager; 89 | productReference = 134814201AA4EA6300B7C361 /* libMultithreading.a */; 90 | productType = "com.apple.product-type.library.static"; 91 | }; 92 | /* End PBXNativeTarget section */ 93 | 94 | /* Begin PBXProject section */ 95 | 58B511D31A9E6C8500147676 /* Project object */ = { 96 | isa = PBXProject; 97 | attributes = { 98 | LastUpgradeCheck = 0920; 99 | ORGANIZATIONNAME = Facebook; 100 | TargetAttributes = { 101 | 58B511DA1A9E6C8500147676 = { 102 | CreatedOnToolsVersion = 6.1.1; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "Multithreading" */; 107 | compatibilityVersion = "Xcode 3.2"; 108 | developmentRegion = English; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | English, 112 | en, 113 | ); 114 | mainGroup = 58B511D21A9E6C8500147676; 115 | productRefGroup = 58B511D21A9E6C8500147676; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | 58B511DA1A9E6C8500147676 /* Multithreading */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXSourcesBuildPhase section */ 125 | 58B511D71A9E6C8500147676 /* Sources */ = { 126 | isa = PBXSourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | 5E555C0D2413F4C50049A1A2 /* RNMultithreading.mm in Sources */, 130 | B887B6332600A8E100C68EE7 /* ThreadPool.cpp in Sources */, 131 | B893C01225FFE54800F249DD /* RNMultithreadingInstaller.cpp in Sources */, 132 | B87D0C2F26174EC7004B892B /* MakeJSIRuntime.cpp in Sources */, 133 | ); 134 | runOnlyForDeploymentPostprocessing = 0; 135 | }; 136 | /* End PBXSourcesBuildPhase section */ 137 | 138 | /* Begin XCBuildConfiguration section */ 139 | 58B511ED1A9E6C8500147676 /* Debug */ = { 140 | isa = XCBuildConfiguration; 141 | buildSettings = { 142 | ALWAYS_SEARCH_USER_PATHS = NO; 143 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 144 | CLANG_CXX_LIBRARY = "libc++"; 145 | CLANG_ENABLE_MODULES = YES; 146 | CLANG_ENABLE_OBJC_ARC = YES; 147 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 148 | CLANG_WARN_BOOL_CONVERSION = YES; 149 | CLANG_WARN_COMMA = YES; 150 | CLANG_WARN_CONSTANT_CONVERSION = YES; 151 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 152 | CLANG_WARN_EMPTY_BODY = YES; 153 | CLANG_WARN_ENUM_CONVERSION = YES; 154 | CLANG_WARN_INFINITE_RECURSION = YES; 155 | CLANG_WARN_INT_CONVERSION = YES; 156 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 157 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 158 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 159 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 160 | CLANG_WARN_STRICT_PROTOTYPES = YES; 161 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 162 | CLANG_WARN_UNREACHABLE_CODE = YES; 163 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 164 | COPY_PHASE_STRIP = NO; 165 | ENABLE_STRICT_OBJC_MSGSEND = YES; 166 | ENABLE_TESTABILITY = YES; 167 | GCC_C_LANGUAGE_STANDARD = gnu99; 168 | GCC_DYNAMIC_NO_PIC = NO; 169 | GCC_NO_COMMON_BLOCKS = YES; 170 | GCC_OPTIMIZATION_LEVEL = 0; 171 | GCC_PREPROCESSOR_DEFINITIONS = ( 172 | "DEBUG=1", 173 | "$(inherited)", 174 | ); 175 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 176 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 177 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 178 | GCC_WARN_UNDECLARED_SELECTOR = YES; 179 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 180 | GCC_WARN_UNUSED_FUNCTION = YES; 181 | GCC_WARN_UNUSED_VARIABLE = YES; 182 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 183 | MTL_ENABLE_DEBUG_INFO = YES; 184 | ONLY_ACTIVE_ARCH = YES; 185 | SDKROOT = iphoneos; 186 | }; 187 | name = Debug; 188 | }; 189 | 58B511EE1A9E6C8500147676 /* Release */ = { 190 | isa = XCBuildConfiguration; 191 | buildSettings = { 192 | ALWAYS_SEARCH_USER_PATHS = NO; 193 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 194 | CLANG_CXX_LIBRARY = "libc++"; 195 | CLANG_ENABLE_MODULES = YES; 196 | CLANG_ENABLE_OBJC_ARC = YES; 197 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 198 | CLANG_WARN_BOOL_CONVERSION = YES; 199 | CLANG_WARN_COMMA = YES; 200 | CLANG_WARN_CONSTANT_CONVERSION = YES; 201 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 209 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 210 | CLANG_WARN_STRICT_PROTOTYPES = YES; 211 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 212 | CLANG_WARN_UNREACHABLE_CODE = YES; 213 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 214 | COPY_PHASE_STRIP = YES; 215 | ENABLE_NS_ASSERTIONS = NO; 216 | ENABLE_STRICT_OBJC_MSGSEND = YES; 217 | GCC_C_LANGUAGE_STANDARD = gnu99; 218 | GCC_NO_COMMON_BLOCKS = YES; 219 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 220 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 221 | GCC_WARN_UNDECLARED_SELECTOR = YES; 222 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 223 | GCC_WARN_UNUSED_FUNCTION = YES; 224 | GCC_WARN_UNUSED_VARIABLE = YES; 225 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 226 | MTL_ENABLE_DEBUG_INFO = NO; 227 | SDKROOT = iphoneos; 228 | VALIDATE_PRODUCT = YES; 229 | }; 230 | name = Release; 231 | }; 232 | 58B511F01A9E6C8500147676 /* Debug */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | HEADER_SEARCH_PATHS = ( 236 | "$(inherited)", 237 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 238 | "$(SRCROOT)/../../../React/**", 239 | "$(SRCROOT)/../../react-native/React/**", 240 | ); 241 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 242 | OTHER_LDFLAGS = "-ObjC"; 243 | PRODUCT_NAME = Multithreading; 244 | SKIP_INSTALL = YES; 245 | SWIFT_OBJC_BRIDGING_HEADER = "Multithreading-Bridging-Header.h"; 246 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 247 | SWIFT_VERSION = 5.0; 248 | }; 249 | name = Debug; 250 | }; 251 | 58B511F11A9E6C8500147676 /* Release */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | HEADER_SEARCH_PATHS = ( 255 | "$(inherited)", 256 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 257 | "$(SRCROOT)/../../../React/**", 258 | "$(SRCROOT)/../../react-native/React/**", 259 | ); 260 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 261 | OTHER_LDFLAGS = "-ObjC"; 262 | PRODUCT_NAME = Multithreading; 263 | SKIP_INSTALL = YES; 264 | SWIFT_OBJC_BRIDGING_HEADER = "Multithreading-Bridging-Header.h"; 265 | SWIFT_VERSION = 5.0; 266 | }; 267 | name = Release; 268 | }; 269 | /* End XCBuildConfiguration section */ 270 | 271 | /* Begin XCConfigurationList section */ 272 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "Multithreading" */ = { 273 | isa = XCConfigurationList; 274 | buildConfigurations = ( 275 | 58B511ED1A9E6C8500147676 /* Debug */, 276 | 58B511EE1A9E6C8500147676 /* Release */, 277 | ); 278 | defaultConfigurationIsVisible = 0; 279 | defaultConfigurationName = Release; 280 | }; 281 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "Multithreading" */ = { 282 | isa = XCConfigurationList; 283 | buildConfigurations = ( 284 | 58B511F01A9E6C8500147676 /* Debug */, 285 | 58B511F11A9E6C8500147676 /* Release */, 286 | ); 287 | defaultConfigurationIsVisible = 0; 288 | defaultConfigurationName = Release; 289 | }; 290 | /* End XCConfigurationList section */ 291 | }; 292 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 293 | } 294 | -------------------------------------------------------------------------------- /ios/RNMultithreading.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RNMultithreading : NSObject 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /ios/RNMultithreading.mm: -------------------------------------------------------------------------------- 1 | #import "RNMultithreading.h" 2 | #import "RNMultithreadingInstaller.h" 3 | 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | 10 | #import 11 | #import 12 | #import "MakeJSIRuntime.h" 13 | 14 | using namespace facebook; 15 | 16 | @implementation RNMultithreading 17 | @synthesize bridge = _bridge; 18 | @synthesize methodQueue = _methodQueue; 19 | 20 | RCT_EXPORT_MODULE() 21 | 22 | + (BOOL)requiresMainQueueSetup { 23 | return YES; 24 | } 25 | 26 | - (void)setBridge:(RCTBridge *)bridge 27 | { 28 | _bridge = bridge; 29 | 30 | RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge; 31 | if (!cxxBridge.runtime) { 32 | return; 33 | } 34 | 35 | auto callInvoker = bridge.jsCallInvoker; 36 | 37 | auto makeRuntime = []() -> std::unique_ptr { 38 | return mrousavy::multithreading::makeJSIRuntime(); 39 | }; 40 | auto makeScheduler = [callInvoker]() -> std::shared_ptr { 41 | return std::make_shared(callInvoker); 42 | }; 43 | auto makeErrorHandler = [](std::shared_ptr scheduler) -> std::shared_ptr { 44 | return std::make_shared(scheduler); 45 | }; 46 | 47 | mrousavy::multithreading::install(*(jsi::Runtime *)cxxBridge.runtime, 48 | makeRuntime, 49 | makeScheduler, 50 | makeErrorHandler); 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-multithreading", 3 | "version": "1.1.1", 4 | "description": "🧵 Fast and easy multithreading for React Native using JSI", 5 | "main": "lib/commonjs/index", 6 | "module": "lib/module/index", 7 | "types": "lib/typescript/index.d.ts", 8 | "react-native": "src/index", 9 | "source": "src/index", 10 | "files": [ 11 | "src", 12 | "lib", 13 | "android", 14 | "ios", 15 | "cpp", 16 | "react-native-multithreading.podspec", 17 | "!lib/typescript/example", 18 | "!android/build", 19 | "!ios/build", 20 | "!**/__tests__", 21 | "!**/__fixtures__", 22 | "!**/__mocks__" 23 | ], 24 | "scripts": { 25 | "bootstrap": "yarn && cd example && yarn && cd ios && pod install", 26 | "typescript": "tsc --noEmit", 27 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 28 | "prepare": "bob build && scripts/build-android.sh", 29 | "prepack": "rm -rf android-backup && mv android android-backup && mv android-npm android", 30 | "postpack": "mv android android-npm && mv android-backup android", 31 | "release": "release-it" 32 | }, 33 | "keywords": [ 34 | "react-native", 35 | "ios", 36 | "android" 37 | ], 38 | "repository": "https://github.com/mrousavy/react-native-multithreading", 39 | "author": "Marc Rousavy (https://github.com/mrousavy)", 40 | "license": "MIT", 41 | "bugs": { 42 | "url": "https://github.com/mrousavy/react-native-multithreading/issues" 43 | }, 44 | "homepage": "https://github.com/mrousavy/react-native-multithreading#readme", 45 | "publishConfig": { 46 | "registry": "https://registry.npmjs.org/" 47 | }, 48 | "devDependencies": { 49 | "@commitlint/config-conventional": "^11.0.0", 50 | "@jamesacarr/eslint-formatter-github-actions": "^0.1.0", 51 | "@react-native-community/eslint-config": "^2.0.0", 52 | "@release-it/conventional-changelog": "^2.0.0", 53 | "@types/jest": "^26.0.0", 54 | "@types/react": "^16.9.19", 55 | "@types/react-native": "^0.64.2", 56 | "commitlint": "^11.0.0", 57 | "eslint": "^7.2.0", 58 | "eslint-config-prettier": "^7.0.0", 59 | "eslint-plugin-prettier": "^3.1.3", 60 | "husky": "^4.2.5", 61 | "jest": "^26.0.1", 62 | "pod-install": "^0.1.0", 63 | "prettier": "^2.0.5", 64 | "react": "17.0.1", 65 | "react-native": "0.64.0", 66 | "react-native-builder-bob": "^0.17.1", 67 | "react-native-reanimated": "^2.1.0", 68 | "release-it": "^14.2.2", 69 | "typescript": "^4.1.3" 70 | }, 71 | "peerDependencies": { 72 | "react": "*", 73 | "react-native": "*", 74 | "react-native-reanimated": ">= 2.1.0" 75 | }, 76 | "jest": { 77 | "preset": "react-native", 78 | "modulePathIgnorePatterns": [ 79 | "/example/node_modules", 80 | "/lib/" 81 | ] 82 | }, 83 | "release-it": { 84 | "git": { 85 | "commitMessage": "chore: release ${version}", 86 | "tagName": "v${version}" 87 | }, 88 | "npm": { 89 | "publish": true 90 | }, 91 | "github": { 92 | "release": true 93 | }, 94 | "plugins": { 95 | "@release-it/conventional-changelog": { 96 | "preset": "angular" 97 | } 98 | } 99 | }, 100 | "eslintConfig": { 101 | "root": true, 102 | "globals": { 103 | "_setGlobalConsole": true, 104 | "_LABEL": true, 105 | "_log": true, 106 | "performance": true 107 | }, 108 | "extends": [ 109 | "@react-native-community", 110 | "prettier" 111 | ], 112 | "rules": { 113 | "prettier/prettier": [ 114 | "error", 115 | { 116 | "quoteProps": "consistent", 117 | "singleQuote": true, 118 | "tabWidth": 2, 119 | "trailingComma": "es5", 120 | "useTabs": false 121 | } 122 | ] 123 | } 124 | }, 125 | "eslintIgnore": [ 126 | "node_modules/", 127 | "lib/" 128 | ], 129 | "prettier": { 130 | "quoteProps": "consistent", 131 | "singleQuote": true, 132 | "tabWidth": 2, 133 | "trailingComma": "es5", 134 | "useTabs": false 135 | }, 136 | "react-native-builder-bob": { 137 | "source": "src", 138 | "output": "lib", 139 | "targets": [ 140 | "commonjs", 141 | "module", 142 | [ 143 | "typescript", 144 | { 145 | "project": "tsconfig.build.json" 146 | } 147 | ] 148 | ] 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /react-native-multithreading.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = "react-native-multithreading" 7 | s.version = package["version"] 8 | s.summary = package["description"] 9 | s.homepage = package["homepage"] 10 | s.license = package["license"] 11 | s.authors = package["author"] 12 | 13 | s.platforms = { :ios => "10.0" } 14 | s.source = { :git => "https://github.com/mrousavy/react-native-multithreading.git", :tag => "#{s.version}" } 15 | 16 | s.pod_target_xcconfig = { 17 | "DEFINES_MODULE" => "YES", 18 | "USE_HEADERMAP" => "YES", 19 | "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " 20 | } 21 | s.requires_arc = true 22 | 23 | s.source_files = "ios/**/*.{h,m,mm}", "cpp/**/*.{h,cpp}" 24 | 25 | s.dependency "React-callinvoker" 26 | s.dependency "React" 27 | s.dependency "React-Core" 28 | end 29 | -------------------------------------------------------------------------------- /scripts/build-android.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CWD="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 4 | 5 | cd $CWD 6 | cd .. 7 | 8 | echo 'Building Android in:' 9 | echo $PWD 10 | 11 | rm -rf android-npm/ 12 | 13 | cd android 14 | rm -rf build/ 15 | 16 | # Build .aar files 17 | ./gradlew clean 18 | ./gradlew assembleDebug assembleRelease 19 | 20 | rm -rf ../android-npm 21 | mkdir ../android-npm 22 | 23 | # Move over the .aar files 24 | shopt -s dotglob nullglob 25 | mv -v build/outputs/aar/* ../android-npm 26 | 27 | # Move over the build.gradle file 28 | cd $CWD 29 | cd .. 30 | cd scripts 31 | cp template.build.gradle ../android-npm/build.gradle 32 | -------------------------------------------------------------------------------- /scripts/template.build.gradle: -------------------------------------------------------------------------------- 1 | configurations.maybeCreate("default") 2 | artifacts.add("default", file('rnmultithreading-debug.aar')) // TODO: Use release variant here? 3 | -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | it.todo('write a test'); 2 | -------------------------------------------------------------------------------- /src/globals.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the current JS-Runtime's label. 3 | * 4 | * * Reanimated UI Runtime: `"UI"` 5 | * * Vision Camera Frame Processor: `"FRAME_PROCESSOR"` 6 | * * Multithreading Thread: `"CUSTOM_THREAD_N"` where `N` is the Thread's number 7 | * * React-JS Runtime: `undefined` 8 | */ 9 | declare var _LABEL: string | undefined; 10 | 11 | /** 12 | * Sets the global console object 13 | */ 14 | declare var _setGlobalConsole: (console: any) => void; 15 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import 'react-native-reanimated'; 2 | import { runOnJS } from 'react-native-reanimated'; 3 | 4 | const g = global as any; 5 | 6 | /** 7 | * Runs the given function in a custom thread, in a custom parallel runtime. 8 | * 9 | * _Note: Despite it's name, Threads are actually not spawned on demand and will be re-used. (Thread-Pool)_ 10 | * 11 | * @example 12 | * ```ts 13 | * const result = await spawnThread(() => { 14 | * const someValue = doExpensiveCalculation() 15 | * return someValue 16 | * }) 17 | * ``` 18 | */ 19 | export const spawnThread = g.spawnThread as (run: () => T) => Promise; 20 | 21 | // TODO: Find a way to automatically bind console once I can spawn multiple threads. Possibly through a member function: Thread.polyfillConsole() 22 | const capturableConsole = console; 23 | spawnThread(() => { 24 | 'worklet'; 25 | const console = { 26 | debug: runOnJS(capturableConsole.debug), 27 | log: runOnJS(capturableConsole.log), 28 | warn: runOnJS(capturableConsole.warn), 29 | error: runOnJS(capturableConsole.error), 30 | info: runOnJS(capturableConsole.info), 31 | }; 32 | _setGlobalConsole(console); 33 | }); 34 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "extends": "./tsconfig", 4 | "exclude": ["example"] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "react-native-multithreading": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "importsNotUsedAsValues": "error", 11 | "forceConsistentCasingInFileNames": true, 12 | "jsx": "react", 13 | "lib": ["esnext"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "resolveJsonModule": true, 23 | "skipLibCheck": true, 24 | "strict": true, 25 | "target": "esnext" 26 | } 27 | } 28 | --------------------------------------------------------------------------------