Android Studio 運行 Apk,不會采用最新構建的 Cocos 內容問題
每次在Cocos構建項目后,在Android Studio上點擊 Run,「可能」會發現這次Run,并不會采用剛剛構建的Cocos內容!??
劃重點: 「可能」
- 有些人/電腦/AndroidStudio版本 會 立即采用剛剛構建的 Cocos 內容
- 有些人/電腦/AndroidStudio版本 不會 立即采用剛剛構建的 Cocos 內容
如果這個能引起你的共鳴,那么不妨試著看下去~
Cocos & Android 構建原理
解決這個問題,我們可以先大概了解一下 Cocos & Android 的一些構建原理:
1.Cocos 構建資源會放在 Android 項目的 assets 資源目錄中
2.app 運行時,讀取 assets 資源目錄,從而獲取到 Cocos 構建后的資源
這里面,我們重點探究的是 Cocos 是怎么將構建的內容放到 Android 的 assets 資源目錄中呢?
這個操作其實在構建后的 app/build.gradle
中,關鍵代碼如下:
android.applicationVariants.all { variant ->
// delete previous files first
delete "${buildDir}/intermediates/merged_assets/${variant.dirName}"
// 復制 Cocos 資源到 Assets 目錄中
variant.mergeAssets.doLast {
// ....
copy {
from "${sourceDir}/res"
into "${outputDir}/res"
}
copy {
from "${sourceDir}/subpackages"
into "${outputDir}/subpackages"
}
copy {
from "${sourceDir}/src"
into "${outputDir}/src"
}
copy {
from "${sourceDir}/jsb-adapter"
into "${outputDir}/jsb-adapter"
}
copy {
from "${sourceDir}/main.js"
from "${sourceDir}/project.json"
into outputDir
}
}
//....
}
怎么理解上面這段代碼呢?
首先 Android 打包 Apk 是由 Google 開發的一套 Gradle 插件去完成打包的。實際上,每次打包會有很多構建任務,比如:編譯java,編譯C++等等。各個任務之間存在依賴關系,插件根據依賴關系執行對應任務,最后就生成了APK。
而其中有一個插件內置的 Android 構建任務 mergeAssets
,其目的在于將 Android 項目及各個依賴 Library 的 Assets 資源文件合并到構建緩存目錄,方便最后打包。
而 Cocos 就是在 這個任務最后(variant.mergeAssets.doLast
),添加了一些復制任務,將 Cocos 的腳本資源等等內容復制到 Android Apk 的 Assets 資源目錄中。
現在,你應該(必須)能很好地理解上面這段代碼以及 Cocos & Android 的構建過程了~
問題成因
那么問題來了?
為什么有些人,他們構建Cocos的游戲后,在Android Studio中運行,每次都能更新 Cocos 構建后的資源,而有些人卻不會更新 Cocos 構建后的資源呢?
實際上,這里用有些人不是很恰當,只是大家習慣于這樣子描述問題。這個問題的差異化在于所用的引擎版本和Android構建Gradle版本,在不同人/PC上可能會采用不同Android Studio版本,不同的構建Gradle版本
。
問題代碼具體為上面的此行代碼
delete "${buildDir}/intermediates/merged_assets/${variant.dirName}"
這個代碼的意圖在于每次復制 Cocos 的資源到 assets 目錄時,先刪除Android之前有可能構建過的 assets 構建暫存目錄,以實現每次打包都能 Copy 最新的 Cocos 構建后的內容進去。
為什么要先刪除 assets 構建暫存目錄呢?這里面,還得插入一個知識點。
Groovy 的 Copy 任務會自動識別本次是否應該執行復制。比如:復制文件A到一個目錄兩次,在第二次執行的時候,同名文件則不會復制,而是會 UP-TO-DATE 不會再執行。
因此為了避免 Cocos 二次構建后的(同名文件)內容也能在 Android Studio 中 Run 的時候也能用上,那么就需要先刪除目錄,讓復制后的目錄沒有同名文件,這樣子在執行復制的時候就會真的是觸發復制邏輯,從而更新最新的 Cocos 構建內容到 Apk 包中。
嗯,這個意圖很清晰,但是上述代碼并不完美。
首先,這種 Hard Code 寫法很容易出現問題,如果路徑換了一下,那么實際上就不能很好的執行這個刪除意圖了。而問題恰好就是這個路徑問題。
實際上,有些同學可能會用不同版本的 Android Studio ,用了不同版本的 Gradle Plugin。而不同版本的 Gradle Plugin 的 mergeAssets
Task 的暫存目錄并不全是上面的絕對路徑。
事實上,不同 Gradle Plugin 版本的 mergeAssets 的輸出路徑有哪些,我也不知道有哪些路徑,并且我們也不應該去固化這個路徑。Android 在持續發展,打包工具也在持續更新,構建任務也可能會修改升級,但這種 Hard Code 路徑的寫法卻極有可能不會能持續用下去。
我們更應該用的是 variant.mergeAssets.outputDir
變量去表示 mergeAssets 的輸出路徑,而不是某個 Hard Code 的字符串。
而 Cocos 恰好是用了 Hard Code 的路徑字符串,從而導致了這個小問題:有些(版本)每次運行都能拿到最新的 Cocos 構建資源,而有些版本缺不可以
解決方案
至此,修復起來就很簡單了,只需要一個很簡單的修改就可以修復:
-- delete "${buildDir}/intermediates/merged_assets/${variant.dirName}"
++ delete variant.mergeAssets.outputDir
當然,這個寫法能持續兼容多久,不能保證,但是相信到時候你已經知道應該怎么解決了