要说最近技术圈什么比较活跃,我想除了动态加载框架和热修复技术之外,非Facebook的React Native莫属了吧,其实RN对IOS的支持比较早,但是Android似乎难产了,直到9月份才刚开源。距离RN开源也有一段时间了,一直没有去学习,今天兴趣来潮,索性学一把吧。
本文假设你的Windows上安装了Android SDK,并配置好了环境变量。
安装Node.js
从官网https://nodejs.org/en/下载Node.js的windows版,也不知道为什么版本迭代这么快,之前安装的时候版本还是v0.12的,如今已经到了v5.1版,直接下载最新版就可以了。安装的时候记得勾选添加到环境变量中去,这样就不用手动添加环境变量了。
安装react-native-cli
打开命令行,输入npm install -g react-native-cli
初始化项目
命令行输入react-native init AwesomeProject,这一步如果卡死,建议翻个墙试试,本人在没翻墙前直接卡死在这一步,后来用了vpn翻了下墙就初始化好了。
启动React Native Server
命令行进入AwesomeProject目录,执行react-native start开启server
导入Android项目
使用Android Studio导入AwesomeProject/android项目,点击run运行(手机和电脑处于同一局域网内)。点击菜单,弹出调试相关的界面。
这里有一个深坑!
这里有一个深坑!
这里有一个深坑!
重要的事情说三次!
由于该调试界面使用的是悬浮窗弹出,而我使用的是小米实体机进行调试的,默认情况下会关闭所有应用的悬浮窗权限,这时候你怎么按菜单键或者玩死里摇手机,该界面都不会出来。解决方法就是在应用权限里开启悬浮窗权限。
之后按菜单键就可以弹出该界面了,修改server地址为你电脑上的ip地址加端口。
这样,运行程序是没问题了。
但是我们需要将其打包进apk,所以就需要用到打包命令。
打包Bundle
进入AwesomeProject目录,命令行执行
react-native bundle –platform android –dev false –entry-file index.android.js –bundle-output C:\Users\Administrator\Desktop\AwesomeProject\android\app\build\intermediates\assets\release\index.android.bundle –assets-dest C:\Users\Administrator\Desktop\AwesomeProject\android\app\build\intermediates\res\merged\release
如果这里你报错了,类似下面的错误
C:\Users\Administrator\Desktop\AwesomeProject>react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output C:\Users\Administrator\Desktop\AwesomeProject\android\app\build\intermediates\assets\release\index.android.bundle --assets-dest C:\Users\Administrator\Desktop\AwesomeProject\android\app\build\intermediates\res\merged\releaseC:\Users\Administrator\Desktop\AwesomeProject\node_modules\promise\lib\done.js:10 throw err; ^Error: Took too long to start server. Server logs:Wed, 18 Nov 2015 05:38:45 GMT ReactNativePackager:SocketServer server got ipc message { type: 'createSocketServer', data: { sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca409637da75daf8b8a17b9bdf33aa', options: { projectRoots: [Object], assetRoots: [], blacklistRE: [Object], transformModulePath: 'C:\\Users\\Administrator\\Desktop\\AwesomeProject\\node_modules\\react-native\\packager\\transformer.js' } } }[13:38:45] <START> Building Dependency Graph[13:38:45] <START> Crawling File System[13:38:45] <START> Loading bundles layout[13:38:45] <END> Loading bundles layout (1ms)Wed, 18 Nov 2015 05:38:45 GMT ReactNativePackager:SocketServer error creating server EACCESWed, 18 Nov 2015 05:38:45 GMT ReactNativePackager:SocketServer uncaught error Error: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca409637da75daf8b8a17b9bdf33aa at Object.exports._errnoException (util.js:856:11) at exports._exceptionWithHostPort (util.js:879:20) at Server._listen2 (net.js:1221:19) at listen (net.js:1270:10) at Server.listen (net.js:1360:5) at new SocketServer (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:18) at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:177:7) at emitTwo (events.js:87:13) at process.emit (events.js:172:7) at handleMessage (internal/child_process.js:686:10)Wed, 18 Nov 2015 05:41:12 GMT ReactNativePackager:SocketServer server got ipc message { type: 'createSocketServer', data: { sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca409637da75daf8b8a17b9bdf33aa', options: { projectRoots: [Object], assetRoots: [], blacklistRE: [Object], transformModulePath: 'C:\\Users\\Administrator\\Desktop\\AwesomeProject\\node_modules\\react-native\\packager\\transformer.js' } } }[13:41:12] <START> Building Dependency Graph[13:41:12] <START> Crawling File System[13:41:12] <START> Loading bundles layout[13:41:12] <END> Loading bundles layout (1ms)Wed, 18 Nov 2015 05:41:12 GMT ReactNativePackager:SocketServer error creating server EACCESWed, 18 Nov 2015 05:41:12 GMT ReactNativePackager:SocketServer uncaught error Error: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca409637da75daf8b8a17b9bdf33aa at Object.exports._errnoException (util.js:856:11) at exports._exceptionWithHostPort (util.js:879:20) at Server._listen2 (net.js:1221:19) at listen (net.js:1270:10) at Server.listen (net.js:1360:5) at new SocketServer (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:18) at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:177:7) at emitTwo (events.js:87:13) at process.emit (events.js:172:7) at handleMessage (internal/child_process.js:686:10)Wed, 18 Nov 2015 05:43:57 GMT ReactNativePackager:SocketServer server got ipc message { type: 'createSocketServer', data: { sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca409637da75daf8b8a17b9bdf33aa', options: { projectRoots: [Object], assetRoots: [], blacklistRE: [Object], transformModulePath: 'C:\\Users\\Administrator\\Desktop\\AwesomeProject\\node_modules\\react-native\\packager\\transformer.js' } } }[13:43:57] <START> Building Dependency Graph[13:43:57] <START> Crawling File System[13:43:57] <START> Loading bundles layout[13:43:57] <END> Loading bundles layout (1ms)Wed, 18 Nov 2015 05:43:57 GMT ReactNativePackager:SocketServer error creating server EACCESWed, 18 Nov 2015 05:43:57 GMT ReactNativePackager:SocketServer uncaught error Error: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca409637da75daf8b8a17b9bdf33aa at Object.exports._errnoException (util.js:856:11) at exports._exceptionWithHostPort (util.js:879:20) at Server._listen2 (net.js:1221:19) at listen (net.js:1270:10) at Server.listen (net.js:1360:5) at new SocketServer (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:18) at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:177:7) at emitTwo (events.js:87:13) at process.emit (events.js:172:7) at handleMessage (internal/child_process.js:686:10)Wed, 18 Nov 2015 05:55:30 GMT ReactNativePackager:SocketServer server got ipc message { type: 'createSocketServer', data: { sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca409637da75daf8b8a17b9bdf33aa', options: { projectRoots: [Object], assetRoots: [], blacklistRE: [Object], transformModulePath: 'C:\\Users\\Administrator\\Desktop\\AwesomeProject\\node_modules\\react-native\\packager\\transformer.js' } } }[13:55:30] <START> Building Dependency Graph[13:55:31] <START> Crawling File System[13:55:31] <START> Loading bundles layout[13:55:31] <END> Loading bundles layout (0ms)Wed, 18 Nov 2015 05:55:31 GMT ReactNativePackager:SocketServer error creating server EACCESWed, 18 Nov 2015 05:55:31 GMT ReactNativePackager:SocketServer uncaught error Error: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca409637da75daf8b8a17b9bdf33aa at Object.exports._errnoException (util.js:856:11) at exports._exceptionWithHostPort (util.js:879:20) at Server._listen2 (net.js:1221:19) at listen (net.js:1270:10) at Server.listen (net.js:1360:5) at new SocketServer (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:18) at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:177:7) at emitTwo (events.js:87:13) at process.emit (events.js:172:7) at handleMessage (internal/child_process.js:686:10)Wed, 18 Nov 2015 05:57:02 GMT ReactNativePackager:SocketServer server got ipc message { type: 'createSocketServer', data: { sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca409637da75daf8b8a17b9bdf33aa', options: { projectRoots: [Object], assetRoots: [], blacklistRE: [Object], transformModulePath: 'C:\\Users\\Administrator\\Desktop\\AwesomeProject\\node_modules\\react-native\\packager\\transformer.js' } } }[13:57:02] <START> Building Dependency Graph[13:57:02] <START> Crawling File System[13:57:02] <START> Loading bundles layout[13:57:02] <END> Loading bundles layout (0ms)Wed, 18 Nov 2015 05:57:02 GMT ReactNativePackager:SocketServer error creating server EACCESWed, 18 Nov 2015 05:57:02 GMT ReactNativePackager:SocketServer uncaught error Error: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca409637da75daf8b8a17b9bdf33aa at Object.exports._errnoException (util.js:856:11) at exports._exceptionWithHostPort (util.js:879:20) at Server._listen2 (net.js:1221:19) at listen (net.js:1270:10) at Server.listen (net.js:1360:5) at new SocketServer (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:18) at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:177:7) at emitTwo (events.js:87:13) at process.emit (events.js:172:7) at handleMessage (internal/child_process.js:686:10) at [object Object]._onTimeout (C:/Users/Administrator/Desktop/AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/index.js:82:7) at Timer.listOnTimeout (timers.js:92:15)
这是个bug,详见https://github.com/facebook/react-native/issues/3997
解决方法就是修改AwesomeProject/node_modules/react-native/packager/react-packager/src/SocketInterface/index.js文件中40行附件的
const sockPath = path.join( tmpdir, 'react-packager-' + hash.digest('hex') );
为下面的内容
let sockPath = path.join( tmpdir, 'react-packager-' + hash.digest('hex') ); if (process.platform==='win32'){ sockPath = sockPath.replace(/^\//, '') sockPath = sockPath.replace(/\//g, '-') sockPath = '\\\\.\\pipe\\' + sockPath }
之后将生成的index.android.bundle拷到assets目录即可。
这里也有一个深坑。
Android Studio中,android目录下的build.gradle会使用react.gradle,该文件中就是打包bundle的task
def config = project.hasProperty("react") ? project.react : [];def bundleAssetName = config.bundleAssetName ?: "index.android.bundle"def entryFile = config.entryFile ?: "index.android.js"// because elvis operatordef elvisFile(thing) { return thing ? file(thing) : null;}def reactRoot = elvisFile(config.root) ?: file("../../")def jsBundleDirDebug = elvisFile(config.jsBundleDirDebug) ?: file("$buildDir/intermediates/assets/debug")def jsBundleDirRelease = elvisFile(config.jsBundleDirRelease) ?: file("$buildDir/intermediates/assets/release")def resourcesDirDebug = elvisFile(config.resourcesDirDebug) ?: file("$buildDir/intermediates/res/merged/debug")def resourcesDirRelease = elvisFile(config.resourcesDirRelease) ?: file("$buildDir/intermediates/res/merged/release")def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"]def jsBundleFileDebug = file("$jsBundleDirDebug/$bundleAssetName")def jsBundleFileRelease = file("$jsBundleDirRelease/$bundleAssetName")task bundleDebugJsAndAssets(type: Exec) { // create dirs if they are not there (e.g. the "clean" task just ran) doFirst { jsBundleDirDebug.mkdirs() resourcesDirDebug.mkdirs() } // set up inputs and outputs so gradle can cache the result inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) outputs.dir jsBundleDirDebug outputs.dir resourcesDirDebug // set up the call to the react-native cli workingDir reactRoot commandLine "react-native", "bundle", "--platform", "android", "--dev", "true", "--entry-file", entryFile, "--bundle-output", jsBundleFileDebug, "--assets-dest", resourcesDirDebug enabled config.bundleInDebug ?: false}task bundleReleaseJsAndAssets(type: Exec) { // create dirs if they are not there (e.g. the "clean" task just ran) doFirst { jsBundleDirRelease.mkdirs() resourcesDirRelease.mkdirs() } // set up inputs and outputs so gradle can cache the result inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) outputs.dir jsBundleDirRelease outputs.dir resourcesDirRelease // set up the call to the react-native cli workingDir reactRoot commandLine "react-native", "bundle", "--platform", "android", "--dev", "false", "--entry-file", entryFile, "--bundle-output", jsBundleFileRelease, "--assets-dest", resourcesDirRelease enabled config.bundleInRelease ?: true}gradle.projectsEvaluated { // hook bundleDebugJsAndAssets into the android build process bundleDebugJsAndAssets.dependsOn mergeDebugResources bundleDebugJsAndAssets.dependsOn mergeDebugAssets processDebugResources.dependsOn bundleDebugJsAndAssets // hook bundleReleaseJsAndAssets into the android build process bundleReleaseJsAndAssets.dependsOn mergeReleaseResources bundleReleaseJsAndAssets.dependsOn mergeReleaseAssets processReleaseResources.dependsOn bundleReleaseJsAndAssets}
如果我们执行bundleReleaseJsAndAssets,或者bundleDebugJsAndAssets这两个task,不知道什么原因,总是报错,但是将对应的命令
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output C:\Users\Administrator\Desktop\AwesomeProject\android\app\build\intermediates\assets\release\index.android.bundle --assets-dest C:\Users\Administrator\Desktop\AwesomeProject\android\app\build\intermediates\res\merged\release
复制到命令行执行,却是可以生成bundle文件的,然而上面的两个task最终执行的就是这条命令,但是无论怎么样它就是报错,最后无奈只能命令行生成,也无伤什么大雅。
如果知道这个问题原因是什么的,也请告知!
- 1楼qq_21430549前天 17:37
- android弟好厉害