記一次在Windows上搭建React Native Android環境踩過的坑
要說最近技術圈什麼比較活躍,我想除了動態載入框架和熱修復技術之外,非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 and
roid --dev false --entry-file index.android.js --bundle-output C:\Users\Administ
rator\Desktop\AwesomeProject\android\app\build\intermediates\assets\release\inde
x.android.bundle --assets-dest C:\Users\Administrator\Desktop\AwesomeProject\and
roid\app\build\intermediates\res\merged\release
C:\Users\Administrator\Desktop\AwesomeProject\node_modules\promise\lib\done.js:1
0
throw err;
^
Error: Took too long to start server. Server logs:
Wed, 18 Nov 2015 05:38:45 GMT ReactNativePackager:SocketServer server got ipc me
ssage { type: 'createSocketServer',
data:
{ sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca40
9637da75daf8b8a17b9bdf33aa',
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 se
rver EACCES
Wed, 18 Nov 2015 05:38:45 GMT ReactNativePackager:SocketServer uncaught error Er
ror: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca40963
7da75daf8b8a17b9bdf33aa
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_modu
les/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:
18)
at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_m
odules/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 me
ssage { type: 'createSocketServer',
data:
{ sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca40
9637da75daf8b8a17b9bdf33aa',
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 se
rver EACCES
Wed, 18 Nov 2015 05:41:12 GMT ReactNativePackager:SocketServer uncaught error Er
ror: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca40963
7da75daf8b8a17b9bdf33aa
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_modu
les/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:
18)
at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_m
odules/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 me
ssage { type: 'createSocketServer',
data:
{ sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca40
9637da75daf8b8a17b9bdf33aa',
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 se
rver EACCES
Wed, 18 Nov 2015 05:43:57 GMT ReactNativePackager:SocketServer uncaught error Er
ror: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca40963
7da75daf8b8a17b9bdf33aa
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_modu
les/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:
18)
at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_m
odules/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 me
ssage { type: 'createSocketServer',
data:
{ sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca40
9637da75daf8b8a17b9bdf33aa',
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 se
rver EACCES
Wed, 18 Nov 2015 05:55:31 GMT ReactNativePackager:SocketServer uncaught error Er
ror: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca40963
7da75daf8b8a17b9bdf33aa
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_modu
les/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:
18)
at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_m
odules/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 me
ssage { type: 'createSocketServer',
data:
{ sockPath: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\react-packager-2bca40
9637da75daf8b8a17b9bdf33aa',
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 se
rver EACCES
Wed, 18 Nov 2015 05:57:02 GMT ReactNativePackager:SocketServer uncaught error Er
ror: listen EACCES C:\Users\ADMINI~1\AppData\Local\Temp\react-packager-2bca40963
7da75daf8b8a17b9bdf33aa
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_modu
les/react-native/packager/react-packager/src/SocketInterface/SocketServer.js:24:
18)
at process.<anonymous> (C:/Users/Administrator/Desktop/AwesomeProject/node_m
odules/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)
解決方法就是修改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 operator
def 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最終執行的就是這條命令,但是無論怎麼樣它就是報錯,最後無奈只能命令列生成,也無傷什麼大雅。
如果知道這個問題原因是什麼的,也請告知!