├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── android ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── reactlibrary │ ├── JSThread.java │ ├── RNThreadModule.java │ ├── RNThreadPackage.java │ ├── ReactContextBuilder.java │ ├── ThreadBaseReactPackage.java │ ├── ThreadSelfModule.java │ └── UIManagerStubModule.java ├── examples └── SimpleExample │ ├── .buckconfig │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .prettierrc.js │ ├── .watchmanconfig │ ├── App.js │ ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── simpleexample │ │ │ │ ├── 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 │ ├── config.js │ ├── index.js │ ├── ios │ ├── Podfile │ ├── Podfile.lock │ ├── SimpleExample-tvOS │ │ └── Info.plist │ ├── SimpleExample-tvOSTests │ │ └── Info.plist │ ├── SimpleExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── SimpleExample-tvOS.xcscheme │ │ │ └── SimpleExample.xcscheme │ ├── SimpleExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── SimpleExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── SimpleExampleTests │ │ ├── Info.plist │ │ └── SimpleExampleTests.m │ ├── metro.config.js │ ├── package.json │ ├── react-native.config.js │ ├── worker.thread.js │ └── yarn.lock ├── index.js ├── ios ├── RNThread.podspec ├── RNThread.xcodeproj │ └── project.pbxproj ├── RNThread.xcworkspace │ └── contents.xcworkspacedata ├── ThreadManager.h ├── ThreadManager.m ├── ThreadSelfManager.h └── WorkerSelfManager.m ├── js ├── Thread.js └── self.js ├── media └── simplethreadexample.gif ├── package.json ├── react-native.config.js └── windows ├── .gitignore ├── .npmignore ├── RNThread.sln └── RNThread ├── Properties ├── AssemblyInfo.cs └── RNThread.rd.xml ├── RNThread.csproj ├── RNThreadModule.cs ├── RNThreadPackage.cs └── project.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | 33 | 34 | # Android/IntelliJ 35 | # 36 | build/ 37 | .idea 38 | .gradle 39 | local.properties 40 | *.iml 41 | 42 | # BUCK 43 | buck-out/ 44 | \.buckd/ 45 | *.keystore 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Travis Nuttall 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 | # react-native-threads 2 | 3 | [![npm version](https://img.shields.io/npm/v/react-native-threads.svg?style=flat-square)](https://www.npmjs.com/package/react-native-threads) 4 | [![downloads](https://img.shields.io/npm/dm/react-native-threads.svg?style=flat-square)](https://www.npmjs.com/package/react-native-threads) 5 | 6 | Spawn new react native JavaScript processes for CPU intensive work outside of your 7 | main UI JavaScript process. 8 | 9 | Despite this package's name, this isn't real 'threading', but rather multi-processing. 10 | The main tradeoff of using this library is memory usage, as creating new JS processes 11 | can have significant overhead. Be sure to benchmark your app's memory usage and other 12 | resources before using this library! Alternative solutions include using `runAfterInteractions` 13 | or the [Interaction Manager](https://facebook.github.io/react-native/docs/interactionmanager.html), 14 | and I recommend you investigate those thoroughly before using this library. 15 | 16 | ## Getting started 17 | 18 | `$ npm install react-native-threads --save` 19 | 20 | ### Mostly automatic installation 21 | 22 | `$ react-native link react-native-threads` 23 | 24 | ### Android 25 | 26 | For android you will need to make a slight modification to your `MainApplication.java` 27 | file. In the `getPackages` method pass in `mReactNativeHost` to the `RNThreadPackage` 28 | constructor: 29 | 30 | ```java 31 | @Override 32 | protected List getPackages() { 33 | return Arrays.asList( 34 | new MainReactPackage(), 35 | new RNThreadPackage(mReactNativeHost) // <-- Here 36 | ); 37 | } 38 | ``` 39 | 40 | Also note that only the official react native modules are available from your 41 | threads (vibration, fetch, etc...). To include additional native modules in your 42 | threads, pass them into the `RNThreadPackage` constructor after the `mReactNativeHost` 43 | like this: 44 | `new RNThreadPackage(mReactNativeHost, new ExampleNativePackage(), new SQLitePackage())` 45 | 46 | ### Manual installation 47 | 48 | 49 | #### iOS 50 | 51 | 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` 52 | 2. Go to `node_modules` ➜ `react-native-threads` and add `RNThread.xcodeproj` 53 | 3. In XCode, in the project navigator, select your project. Add `libRNThread.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` 54 | 4. Run your project (`Cmd+R`)< 55 | 56 | #### Android 57 | 58 | 1. Open up `android/app/src/main/java/[...]/MainApplication.java` 59 | - Add `import com.reactlibrary.RNThreadPackage;` to the imports at the top of the file 60 | - Add `new RNThreadPackage(mReactNativeHost)` to the list returned by the `getPackages()` method 61 | - Also note that only the official react native modules are available from your 62 | threads (vibration, fetch, etc...). To include additional native modules in your 63 | threads, pass them into the `RNThreadPackage` constructor after the `mReactNativeHost` 64 | like this: 65 | `new RNThreadPackage(mReactNativeHost, new ExampleNativePackage(), new SQLitePackage())` 66 | 67 | 2. Append the following lines to `android/settings.gradle`: 68 | ``` 69 | include ':react-native-threads' 70 | project(':react-native-threads').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-threads/android') 71 | ``` 72 | 3. Insert the following lines inside the dependencies block in `android/app/build.gradle`: 73 | ``` 74 | compile project(':react-native-threads') 75 | ``` 76 | 77 | #### Windows 78 | Windows support is not yet implemented, but PRs are welcome if you want to give it a shot! 79 | 80 | [Read it! :D](https://github.com/ReactWindows/react-native) 81 | 82 | 1. In Visual Studio add the `RNThread.sln` in `node_modules/react-native-threads/windows/RNThread.sln` folder to their solution, reference from their app. 83 | 2. Open up your `MainPage.cs` app 84 | - Add `using Thread.RNThread;` to the usings at the top of the file 85 | - Add `new RNThreadPackage()` to the `List` returned by the `Packages` method 86 | 87 | 88 | ## Usage 89 | 90 | In your application code (react components, etc.): 91 | 92 | ```javascript 93 | import { Thread } from 'react-native-threads'; 94 | 95 | // start a new react native JS process 96 | const thread = new Thread('path/to/thread.js'); 97 | 98 | // send a message, strings only 99 | thread.postMessage('hello'); 100 | 101 | // listen for messages 102 | thread.onmessage = (message) => console.log(message); 103 | 104 | // stop the JS process 105 | thread.terminate(); 106 | ``` 107 | 108 | In your thread code (dedicated file such as `thread.js`): 109 | ```javascript 110 | import { self } from 'react-native-threads'; 111 | 112 | // listen for messages 113 | self.onmessage = (message) => { 114 | } 115 | 116 | // send a message, strings only 117 | self.postMessage('hello'); 118 | ``` 119 | 120 | Check out the examples directory in this repo for demos of using `react-native-threads` 121 | in a functioning app! 122 | 123 | ### Thread Lifecycle 124 | 125 | - Threads are paused when the app enters in the background 126 | - Threads are resumed once the app is running in the foreground 127 | - During development, when you reload the main JS bundle (shake device -> `Reload`) the threads are killed 128 | 129 | ### Debugging 130 | 131 | Instantiating Threads creates multiple react native JS processes and can make debugging 132 | remotely behave unpredictably. I recommend using a third party debugging tool like 133 | [Reactotron](https://github.com/infinitered/reactotron) to aid with this. Each process, 134 | including your main application as well as your thread code can connect to Reactotron 135 | and log debugging messages. 136 | 137 | ### Building for Release 138 | 139 | You will need to manually bundle your thread files for use in a production release 140 | of your app. This documentation assumes you have a single thread file called 141 | `index.thread.js` in your project root. If your file is named differently or in 142 | a different location, you can update the documented commands accordingly. 143 | 144 | **Note**: If your single thread file is in a different location, the folder structure needs to 145 | be replicated under `./ios` and `./android/app/src/main/assets/threads`. 146 | 147 | ``` 148 | ./App/Workers/worker.thread.js => ./ios/App/Workers/worker.thread.jsbundle 149 | ./App/Workers/worker.thread.js => ./android/app/src/main/assets/threads/App/Workers/worker.thread.jsbundle 150 | ``` 151 | 152 | For iOS you can use the following command: 153 | 154 | `node node_modules/react-native/local-cli/cli.js bundle --dev false --assets-dest ./ios --entry-file index.thread.js --platform ios --bundle-output ./ios/index.thread.jsbundle` 155 | 156 | Once you have generated the bundle file in your ios folder, you will also need to add 157 | the bundle file to you project in Xcode. In Xcode's file explorer you should see 158 | a folder with the same name as your app, containing a `main.jsbundle` file as well 159 | as an `appDelegate.m` file. Right click on that folder and select the 'Add Files to ' 160 | option, which will open up finder and allow you to select your `ios/index.thread.jsbundle` 161 | file. You will only need to do this once, and the file will be included in all future 162 | builds. 163 | 164 | For Android create this direactory 165 | `mkdir ./android/app/src/main/assets/threads` 166 | 167 | And then you can use the following command: 168 | 169 | `node node_modules/react-native/local-cli/cli.js bundle --dev false --assets-dest ./android/app/src/main/res/ --entry-file index.thread.js --platform android --bundle-output ./android/app/src/main/assets/threads/index.thread.bundle` 170 | 171 | For convenience I recommend adding these thread building commands as npm scripts 172 | to your project. 173 | 174 | ## Example App 175 | Included in this repository is a simple example application demonstrating basic 176 | usage of react-native-threads. Look at `examples/SimpleExample/README.md` for 177 | instructions on running it. Here's how the app looks with the Reactotron debugger: 178 | 179 | ![SimpleExample Screen Capture](https://raw.githubusercontent.com/traviskn/react-native-threads/master/media/simplethreadexample.gif) 180 | 181 | ## Acknowledgements 182 | 183 | This library was heavily inspired by two other packages both under the name of 184 | `react-native-workers`. 185 | 186 | The first was https://github.com/fabriciovergal/react-native-workers , 187 | and the second was https://github.com/devfd/react-native-workers 188 | 189 | I ended up going with devfd's implementation strategy as it seemed more flexible 190 | and feature-rich to me. At the time of this writing neither library was functioning 191 | on the latest version of react native, and neither seemed to be very actively maintained. 192 | 193 | This library would not exist without those two reference implementations to guide me! 194 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.library' 13 | 14 | def safeExtGet(prop, fallback) { 15 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 16 | } 17 | 18 | android { 19 | compileSdkVersion safeExtGet('compileSdkVersion', 23) 20 | 21 | defaultConfig { 22 | minSdkVersion safeExtGet('minSdkVersion', 16) 23 | targetSdkVersion safeExtGet('targetSdkVersion', 22) 24 | versionCode 1 25 | versionName "1.0" 26 | } 27 | lintOptions { 28 | abortOnError false 29 | } 30 | } 31 | 32 | repositories { 33 | mavenCentral() 34 | jcenter() 35 | maven { 36 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 37 | url "$rootDir/../node_modules/react-native/android" 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation 'com.facebook.react:react-native:+' 43 | } 44 | 45 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joltup/react-native-threads/e8a9d68cd9b0a7d289ead366e6a427339f875f4c/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 16 10:14:34 MDT 2017 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-3.3-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/JSThread.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.modules.core.DeviceEventManagerModule; 5 | 6 | import java.util.Random; 7 | 8 | public class JSThread { 9 | private int id; 10 | 11 | private String jsSlugname; 12 | private ReactApplicationContext reactContext; 13 | 14 | public JSThread(String jsSlugname) { 15 | this.id = Math.abs(new Random().nextInt()); 16 | this.jsSlugname = jsSlugname; 17 | } 18 | 19 | public int getThreadId() { 20 | return this.id; 21 | } 22 | 23 | public String getName() { 24 | return jsSlugname; 25 | } 26 | 27 | public void runFromContext(ReactApplicationContext context, ReactContextBuilder reactContextBuilder) throws Exception { 28 | if (reactContext != null) { 29 | return; 30 | } 31 | 32 | reactContext = reactContextBuilder.build(); 33 | 34 | ThreadSelfModule threadSelfModule = reactContext.getNativeModule(ThreadSelfModule.class); 35 | threadSelfModule.initialize(id, context); 36 | } 37 | 38 | public void postMessage(String message) { 39 | if (reactContext == null) { 40 | return; 41 | } 42 | 43 | reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 44 | .emit("ThreadMessage", message); 45 | } 46 | 47 | public void onHostResume() { 48 | if (reactContext == null) { 49 | return; 50 | } 51 | 52 | reactContext.onHostResume(null); 53 | } 54 | 55 | public void onHostPause() { 56 | if (reactContext == null) { 57 | return; 58 | } 59 | 60 | reactContext.onHostPause(); 61 | } 62 | 63 | public void terminate() { 64 | if (reactContext == null) { 65 | return; 66 | } 67 | 68 | reactContext.onHostPause(); 69 | reactContext.destroy(); 70 | reactContext = null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/RNThreadModule.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.util.Log; 6 | 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactInstanceManager; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.react.bridge.JSBundleLoader; 11 | import com.facebook.react.bridge.LifecycleEventListener; 12 | import com.facebook.react.bridge.Promise; 13 | import com.facebook.react.bridge.ReactApplicationContext; 14 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 15 | import com.facebook.react.bridge.ReactMethod; 16 | import com.facebook.react.devsupport.interfaces.DevSupportManager; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.HashMap; 23 | 24 | 25 | import okhttp3.OkHttpClient; 26 | import okhttp3.Request; 27 | import okhttp3.Response; 28 | import okio.Okio; 29 | import okio.Sink; 30 | 31 | 32 | public class RNThreadModule extends ReactContextBaseJavaModule implements LifecycleEventListener { 33 | 34 | private String TAG = "ThreadManager"; 35 | private HashMap threads; 36 | 37 | private ReactApplicationContext reactApplicationContext; 38 | 39 | private ReactNativeHost reactNativeHost; 40 | 41 | private ReactPackage additionalThreadPackages[]; 42 | 43 | public RNThreadModule(final ReactApplicationContext reactContext, ReactNativeHost reactNativehost, ReactPackage additionalThreadPackages[]) { 44 | super(reactContext); 45 | this.reactApplicationContext = reactContext; 46 | threads = new HashMap<>(); 47 | this.reactNativeHost = reactNativehost; 48 | this.additionalThreadPackages = additionalThreadPackages; 49 | reactContext.addLifecycleEventListener(this); 50 | } 51 | 52 | @Override 53 | public String getName() { 54 | return "ThreadManager"; 55 | } 56 | 57 | @ReactMethod 58 | public void startThread(final String jsFileName, final Promise promise) { 59 | Log.d(TAG, "Starting web thread - " + jsFileName); 60 | 61 | // When we create the absolute file path later, a "./" will break it. 62 | // Remove the leading "./" if it exists. 63 | String jsFileSlug = jsFileName.contains("./") 64 | ? jsFileName.replace("./", "") 65 | : jsFileName; 66 | 67 | JSBundleLoader bundleLoader = getDevSupportManager().getDevSupportEnabled() 68 | ? createDevBundleLoader(jsFileName, jsFileSlug) 69 | : createReleaseBundleLoader(jsFileName, jsFileSlug); 70 | 71 | try { 72 | ArrayList threadPackages = new ArrayList(Arrays.asList(additionalThreadPackages)); 73 | threadPackages.add(0, new ThreadBaseReactPackage(getReactInstanceManager())); 74 | 75 | ReactContextBuilder threadContextBuilder = new ReactContextBuilder(getReactApplicationContext()) 76 | .setJSBundleLoader(bundleLoader) 77 | .setDevSupportManager(getDevSupportManager()) 78 | .setReactInstanceManager(getReactInstanceManager()) 79 | .setReactPackages(threadPackages); 80 | 81 | JSThread thread = new JSThread(jsFileSlug); 82 | thread.runFromContext( 83 | getReactApplicationContext(), 84 | threadContextBuilder 85 | ); 86 | threads.put(thread.getThreadId(), thread); 87 | promise.resolve(thread.getThreadId()); 88 | } catch (Exception e) { 89 | promise.reject(e); 90 | getDevSupportManager().handleException(e); 91 | } 92 | } 93 | 94 | @ReactMethod 95 | public void stopThread(final int threadId) { 96 | final JSThread thread = threads.get(threadId); 97 | if (thread == null) { 98 | Log.d(TAG, "Cannot stop thread - thread is null for id " + threadId); 99 | return; 100 | } 101 | 102 | new Handler(Looper.getMainLooper()).post(new Runnable() { 103 | @Override 104 | public void run() { 105 | thread.terminate(); 106 | threads.remove(threadId); 107 | } 108 | }); 109 | } 110 | 111 | @ReactMethod 112 | public void postThreadMessage(int threadId, String message) { 113 | JSThread thread = threads.get(threadId); 114 | if (thread == null) { 115 | Log.d(TAG, "Cannot post message to thread - thread is null for id " + threadId); 116 | return; 117 | } 118 | 119 | thread.postMessage(message); 120 | } 121 | 122 | @Override 123 | public void onHostResume() { 124 | new Handler(Looper.getMainLooper()).post(new Runnable() { 125 | @Override 126 | public void run() { 127 | for (int threadId : threads.keySet()) { 128 | threads.get(threadId).onHostResume(); 129 | } 130 | } 131 | }); 132 | } 133 | 134 | @Override 135 | public void onHostPause() { 136 | new Handler(Looper.getMainLooper()).post(new Runnable() { 137 | @Override 138 | public void run() { 139 | for (int threadId : threads.keySet()) { 140 | threads.get(threadId).onHostPause(); 141 | } 142 | } 143 | }); 144 | } 145 | 146 | @Override 147 | public void onHostDestroy() { 148 | Log.d(TAG, "onHostDestroy - Clean JS Threads"); 149 | 150 | new Handler(Looper.getMainLooper()).post(new Runnable() { 151 | @Override 152 | public void run() { 153 | for (int threadId : threads.keySet()) { 154 | threads.get(threadId).terminate(); 155 | } 156 | } 157 | }); 158 | } 159 | 160 | @Override 161 | public void onCatalystInstanceDestroy() { 162 | super.onCatalystInstanceDestroy(); 163 | onHostDestroy(); 164 | } 165 | 166 | /* 167 | * Helper methods 168 | */ 169 | 170 | private JSBundleLoader createDevBundleLoader(String jsFileName, String jsFileSlug) { 171 | String bundleUrl = bundleUrlForFile(jsFileName); 172 | // nested file directory will not exist in the files dir during development, 173 | // so remove any leading directory paths to simply download a flat file into 174 | // the root of the files directory. 175 | String[] splitFileSlug = jsFileSlug.split("/"); 176 | String bundleOut = getReactApplicationContext().getFilesDir().getAbsolutePath() + "/" + splitFileSlug[splitFileSlug.length - 1]; 177 | 178 | Log.d(TAG, "createDevBundleLoader - download web thread to - " + bundleOut); 179 | downloadScriptToFileSync(bundleUrl, bundleOut); 180 | 181 | return JSBundleLoader.createCachedBundleFromNetworkLoader(bundleUrl, bundleOut); 182 | } 183 | 184 | private JSBundleLoader createReleaseBundleLoader(String jsFileName, String jsFileSlug) { 185 | Log.d(TAG, "createReleaseBundleLoader - reading file from assets"); 186 | return JSBundleLoader.createAssetLoader(reactApplicationContext, "assets://threads/" + jsFileSlug + ".bundle", false); 187 | } 188 | 189 | private ReactInstanceManager getReactInstanceManager() { 190 | return reactNativeHost.getReactInstanceManager(); 191 | } 192 | 193 | private DevSupportManager getDevSupportManager() { 194 | return getReactInstanceManager().getDevSupportManager(); 195 | } 196 | 197 | private String bundleUrlForFile(final String fileName) { 198 | // http://localhost:8081/index.android.bundle?platform=android&dev=true&hot=false&minify=false 199 | String sourceUrl = getDevSupportManager().getSourceUrl().replace("http://", ""); 200 | return "http://" 201 | + sourceUrl.split("/")[0] 202 | + "/" 203 | + fileName 204 | + ".bundle?platform=android&dev=true&hot=false&minify=false"; 205 | } 206 | 207 | private void downloadScriptToFileSync(String bundleUrl, String bundleOut) { 208 | OkHttpClient client = new OkHttpClient(); 209 | final File out = new File(bundleOut); 210 | 211 | Request request = new Request.Builder() 212 | .url(bundleUrl) 213 | .build(); 214 | 215 | try { 216 | Response response = client.newCall(request).execute(); 217 | if (!response.isSuccessful()) { 218 | throw new RuntimeException("Error downloading thread script - " + response.toString()); 219 | } 220 | 221 | Sink output = Okio.sink(out); 222 | Okio.buffer(response.body().source()).readAll(output); 223 | } catch (IOException e) { 224 | throw new RuntimeException("Exception downloading thread script to file", e); 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/RNThreadPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.ReactNativeHost; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class RNThreadPackage implements ReactPackage { 14 | 15 | private ReactNativeHost reactNativeHost; 16 | private ReactPackage additionalThreadPackages[]; 17 | 18 | public RNThreadPackage(ReactNativeHost reactNativeHost, ReactPackage... additionalThreadPackages) { 19 | this.reactNativeHost = reactNativeHost; 20 | this.additionalThreadPackages = additionalThreadPackages; 21 | } 22 | 23 | @Override 24 | public List createViewManagers(ReactApplicationContext reactContext) { 25 | return Collections.emptyList(); 26 | } 27 | 28 | @Override 29 | public List createNativeModules(ReactApplicationContext reactContext) { 30 | return Arrays.asList( 31 | new RNThreadModule(reactContext, reactNativeHost, additionalThreadPackages) 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/ReactContextBuilder.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | 6 | import com.facebook.hermes.reactexecutor.HermesExecutorFactory; 7 | import com.facebook.react.NativeModuleRegistryBuilder; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.CatalystInstance; 10 | import com.facebook.react.bridge.CatalystInstanceImpl; 11 | import com.facebook.react.bridge.JSBundleLoader; 12 | import com.facebook.react.bridge.JavaScriptExecutorFactory; 13 | import com.facebook.react.jscexecutor.JSCExecutorFactory; 14 | import com.facebook.react.bridge.JavaScriptExecutor; 15 | import com.facebook.react.bridge.NativeModuleCallExceptionHandler; 16 | import com.facebook.react.bridge.ReactApplicationContext; 17 | import com.facebook.react.ReactInstanceManager; 18 | import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; 19 | import com.facebook.react.devsupport.interfaces.DevSupportManager; 20 | import com.facebook.soloader.SoLoader; 21 | 22 | import java.util.ArrayList; 23 | import java.util.concurrent.Callable; 24 | 25 | import static com.facebook.react.modules.systeminfo.AndroidInfoHelpers.getFriendlyDeviceName; 26 | 27 | public class ReactContextBuilder { 28 | 29 | private Context parentContext; 30 | private JSBundleLoader jsBundleLoader; 31 | private DevSupportManager devSupportManager; 32 | private ReactInstanceManager instanceManager; 33 | private ArrayList reactPackages; 34 | 35 | public ReactContextBuilder(Context context) { 36 | this.parentContext = context; 37 | SoLoader.init(context, /* native exopackage */ false); 38 | } 39 | 40 | public ReactContextBuilder setJSBundleLoader(JSBundleLoader jsBundleLoader) { 41 | this.jsBundleLoader = jsBundleLoader; 42 | return this; 43 | } 44 | 45 | public ReactContextBuilder setDevSupportManager(DevSupportManager devSupportManager) { 46 | this.devSupportManager = devSupportManager; 47 | return this; 48 | } 49 | 50 | public ReactContextBuilder setReactInstanceManager(ReactInstanceManager manager) { 51 | this.instanceManager = manager; 52 | return this; 53 | } 54 | 55 | public ReactContextBuilder setReactPackages(ArrayList reactPackages) { 56 | this.reactPackages = reactPackages; 57 | return this; 58 | } 59 | 60 | private JavaScriptExecutorFactory getJSExecutorFactory() { 61 | try { 62 | String appName = Uri.encode(parentContext.getPackageName()); 63 | String deviceName = Uri.encode(getFriendlyDeviceName()); 64 | // If JSC is included, use it as normal 65 | SoLoader.loadLibrary("jscexecutor"); 66 | return new JSCExecutorFactory(appName, deviceName); 67 | } catch (UnsatisfiedLinkError jscE) { 68 | // Otherwise use Hermes 69 | return new HermesExecutorFactory(); 70 | } 71 | } 72 | 73 | public ReactApplicationContext build() throws Exception { 74 | JavaScriptExecutor jsExecutor = getJSExecutorFactory().create(); 75 | 76 | // fresh new react context 77 | final ReactApplicationContext reactContext = new ReactApplicationContext(parentContext); 78 | if (devSupportManager != null) { 79 | reactContext.setNativeModuleCallExceptionHandler(devSupportManager); 80 | } 81 | 82 | // load native modules 83 | NativeModuleRegistryBuilder nativeRegistryBuilder = new NativeModuleRegistryBuilder(reactContext, this.instanceManager); 84 | addNativeModules(nativeRegistryBuilder); 85 | 86 | CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder() 87 | .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) 88 | .setJSExecutor(jsExecutor) 89 | .setRegistry(nativeRegistryBuilder.build()) 90 | .setJSBundleLoader(jsBundleLoader) 91 | .setNativeModuleCallExceptionHandler(devSupportManager != null 92 | ? devSupportManager 93 | : createNativeModuleExceptionHandler() 94 | ); 95 | 96 | 97 | final CatalystInstance catalystInstance; 98 | catalystInstance = catalystInstanceBuilder.build(); 99 | 100 | catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue( 101 | new Callable() { 102 | @Override 103 | public Object call() throws Exception { 104 | try { 105 | reactContext.initializeWithInstance(catalystInstance); 106 | catalystInstance.runJSBundle(); 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | devSupportManager.handleException(e); 110 | } 111 | 112 | return null; 113 | } 114 | } 115 | ).get(); 116 | 117 | catalystInstance.getReactQueueConfiguration().getUIQueueThread().callOnQueue(new Callable() { 118 | @Override 119 | public Object call() throws Exception { 120 | try { 121 | catalystInstance.initialize(); 122 | reactContext.onHostResume(null); 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | devSupportManager.handleException(e); 126 | } 127 | 128 | return null; 129 | } 130 | }).get(); 131 | 132 | return reactContext; 133 | } 134 | 135 | private NativeModuleCallExceptionHandler createNativeModuleExceptionHandler() { 136 | return new NativeModuleCallExceptionHandler() { 137 | @Override 138 | public void handleException(Exception e) { 139 | throw new RuntimeException(e); 140 | } 141 | }; 142 | } 143 | 144 | private void addNativeModules(NativeModuleRegistryBuilder nativeRegistryBuilder) { 145 | for (int i = 0; i < reactPackages.size(); i++) { 146 | ReactPackage reactPackage = reactPackages.get(i); 147 | nativeRegistryBuilder.processPackage(reactPackage); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/ThreadBaseReactPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import com.facebook.react.ReactInstanceManager; 4 | import com.facebook.react.ReactPackage; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.devsupport.JSCHeapCapture; 8 | import com.facebook.react.modules.appstate.AppStateModule; 9 | import com.facebook.react.modules.core.ExceptionsManagerModule; 10 | import com.facebook.react.modules.core.TimingModule; 11 | import com.facebook.react.modules.debug.SourceCodeModule; 12 | import com.facebook.react.modules.intent.IntentModule; 13 | import com.facebook.react.modules.network.NetworkingModule; 14 | import com.facebook.react.modules.storage.AsyncStorageModule; 15 | import com.facebook.react.modules.systeminfo.AndroidInfoModule; 16 | import com.facebook.react.modules.vibration.VibrationModule; 17 | import com.facebook.react.modules.websocket.WebSocketModule; 18 | import com.facebook.react.uimanager.ViewManager; 19 | import com.facebook.react.modules.debug.DevSettingsModule; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | public class ThreadBaseReactPackage implements ReactPackage { 26 | 27 | private final ReactInstanceManager reactInstanceManager; 28 | 29 | public ThreadBaseReactPackage(ReactInstanceManager reactInstanceManager) { 30 | this.reactInstanceManager = reactInstanceManager; 31 | } 32 | 33 | @Override 34 | public List createNativeModules(ReactApplicationContext catalystApplicationContext) { 35 | return Arrays.asList( 36 | // Core list 37 | new AndroidInfoModule(catalystApplicationContext), 38 | new ExceptionsManagerModule(reactInstanceManager.getDevSupportManager()), 39 | new AppStateModule(catalystApplicationContext), 40 | new TimingModule(catalystApplicationContext, reactInstanceManager.getDevSupportManager()), 41 | new UIManagerStubModule(catalystApplicationContext), 42 | new SourceCodeModule(catalystApplicationContext), 43 | new JSCHeapCapture(catalystApplicationContext), 44 | 45 | // Main list 46 | new AsyncStorageModule(catalystApplicationContext), 47 | new IntentModule(catalystApplicationContext), 48 | new NetworkingModule(catalystApplicationContext), 49 | new VibrationModule(catalystApplicationContext), 50 | new WebSocketModule(catalystApplicationContext), 51 | new ThreadSelfModule(catalystApplicationContext), 52 | new DevSettingsModule(catalystApplicationContext, reactInstanceManager.getDevSupportManager()) 53 | ); 54 | } 55 | 56 | @Override 57 | public List createViewManagers(ReactApplicationContext reactContext) { 58 | return new ArrayList<>(0); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/ThreadSelfModule.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 5 | import com.facebook.react.bridge.ReactMethod; 6 | import com.facebook.react.module.annotations.ReactModule; 7 | import com.facebook.react.modules.core.DeviceEventManagerModule; 8 | 9 | @ReactModule(name = ThreadSelfModule.REACT_MODULE_NAME) 10 | public class ThreadSelfModule extends ReactContextBaseJavaModule { 11 | public static final String REACT_MODULE_NAME = "ThreadSelfManager"; 12 | 13 | private int threadId; 14 | private ReactApplicationContext parentContext; 15 | 16 | public ThreadSelfModule(ReactApplicationContext context) { 17 | super(context); 18 | } 19 | 20 | public void initialize(int threadId, ReactApplicationContext parentContext) { 21 | this.parentContext = parentContext; 22 | this.threadId = threadId; 23 | } 24 | 25 | @Override 26 | public String getName() { 27 | return REACT_MODULE_NAME; 28 | } 29 | 30 | @ReactMethod 31 | public void postMessage(String data) { 32 | if (parentContext == null) { return; } 33 | 34 | parentContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 35 | .emit("Thread" + String.valueOf(threadId), data); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/UIManagerStubModule.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import com.facebook.react.bridge.OnBatchCompleteListener; 4 | import com.facebook.react.bridge.ReactApplicationContext; 5 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 6 | 7 | public class UIManagerStubModule extends ReactContextBaseJavaModule implements OnBatchCompleteListener { 8 | 9 | public UIManagerStubModule(ReactApplicationContext reactContext) { 10 | super(reactContext); 11 | } 12 | 13 | @Override 14 | public String getName() { 15 | return "UIManager"; 16 | } 17 | 18 | @Override 19 | public void onBatchComplete() { 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/SimpleExample/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /examples/SimpleExample/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/Libraries/react-native/react-native-interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native$' -> '/node_modules/react-native/Libraries/react-native/react-native-implementation' 40 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 41 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 42 | 43 | suppress_type=$FlowIssue 44 | suppress_type=$FlowFixMe 45 | suppress_type=$FlowFixMeProps 46 | suppress_type=$FlowFixMeState 47 | 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 50 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 51 | 52 | [lints] 53 | sketchy-null-number=warn 54 | sketchy-null-mixed=warn 55 | sketchy-number=warn 56 | untyped-type-import=warn 57 | nonstrict-import=warn 58 | deprecated-type=warn 59 | unsafe-getters-setters=warn 60 | inexact-spread=warn 61 | unnecessary-invariant=warn 62 | signature-verification-failure=warn 63 | deprecated-utility=error 64 | 65 | [strict] 66 | deprecated-type 67 | nonstrict-import 68 | sketchy-null 69 | unclear-type 70 | unsafe-getters-setters 71 | untyped-import 72 | untyped-type-import 73 | 74 | [version] 75 | ^0.105.0 76 | -------------------------------------------------------------------------------- /examples/SimpleExample/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /examples/SimpleExample/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /examples/SimpleExample/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /examples/SimpleExample/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/SimpleExample/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | Button, 4 | StyleSheet, 5 | Text, 6 | View 7 | } from 'react-native'; 8 | import { Thread } from 'react-native-threads'; 9 | import './config'; 10 | 11 | export default class App extends Component { 12 | state = { messages: [] } 13 | 14 | workerThread = null; 15 | 16 | componentDidMount() { 17 | this.workerThread = new Thread('./worker.thread.js'); 18 | this.workerThread.onmessage = this.handleMessage; 19 | } 20 | 21 | componentWillUnmount() { 22 | this.workerThread.terminate(); 23 | this.workerThread = null; 24 | } 25 | 26 | handleMessage = message => { 27 | console.tron.log(`APP: got message ${message}`); 28 | 29 | this.setState(state => { 30 | return { messages: [...state.messages, message] }; 31 | }); 32 | } 33 | 34 | render() { 35 | return ( 36 | 37 | 38 | Welcome to React Native Threads! 39 | 40 | 41 |