При разработке с Eclipse, если я запускал/отлаживал приложение до и не изменял его исходный код, достаточно быстро запустить/отладить одно и то же приложение.
Однако, при использовании Android Studio и Gralde, каждый раз, когда я пытаюсь запустить/отлаживать приложение, сборка gradle всегда будет выполняться, добавив дополнительную задержку в 15-45 секунд до момента запуска приложения (и иногда до 70 секунд на 4-летнем ноутбуке HP i7).
Поэтому возникает вопрос: есть ли способ пропустить фазу сборки Android Studio gradle или хотя бы сократить время, затрачиваемое на запуск/отладку?
Примечание. Я уже настроил gradle.properties следующим образом:
org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.configureondemand=true
Изменить: Моя сборка gradle, вероятно, более сложна, чем большинство проектов, так как она имеет 7 разных вкусов (будет расширяться до ~ 20) и 3 типа сборки, а также содержать Groovy код изменить имя APK (вставить текущую дату) и автоматически вставлять задачи, чтобы увеличить код версии и имя версии в зависимости от текущего типа buildType. Здесь полный build.gradle(измененный, чтобы скрыть имена клиентов):
import java.text.SimpleDateFormat
apply plugin: 'com.android.application'
def appendVersionNameVersionCode(applicationVariants) {
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null) {
def PREFIX = "My_APP_"
if (outputFile.name.endsWith('.apk') && !outputFile.name.startsWith(PREFIX)) {
def names = variant.baseName.split("-");
def apkName = PREFIX+names[0]+"_";
if(names[1].equals(android.buildTypes.debugEx.name)) {
apkName += 'debugEx_'
} else {
apkName += new SimpleDateFormat("YYYYMMdd").format(new Date())
}
if(variant.name.toLowerCase().contains(android.buildTypes.release.name)) {
if (outputFile.name.contains('unsigned')) {
apkName += "-unsigned"
} else {
apkName += "_SIGNED"
}
}
if (!variant.outputs.zipAlign) {
apkName += "-unaligned"
}
apkName += ".apk"
println outputFile.name+" --> " + apkName
output.outputFile = new File(outputFile.parent, apkName)
}
}
}
}
}
def retrieveVersionCode(variantName) {
def manifestFile = file("src/$variantName/AndroidManifest.xml")
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def manifestText = manifestFile.getText()
def matcher = pattern.matcher(manifestText)
matcher.find()
return Integer.parseInt(matcher.group(1))
}
def retrieveVersionName(variantName) {
def manifestFile = file("src/$variantName/AndroidManifest.xml")
def pattern = Pattern.compile(Pattern.quote("versionName=\"") + "(.*?)"+ Pattern.quote("\""))
def manifestText = manifestFile.getText()
def matcher = pattern.matcher(manifestText)
matcher.find()
return matcher.group(1)
}
android {
compileSdkVersion 21
buildToolsVersion "21.1.0"
lintOptions {
abortOnError false
absolutePaths false
lintConfig file("lint.xml")
}
defaultConfig {
applicationId "com.app.sportcam"
minSdkVersion 8
targetSdkVersion 21
}
if(project.hasProperty("app.signing")
&& new File(project.property("app.signing")+'.gradle').exists()) {
apply from: project.property("app.signing")+'.gradle';
} else {
println 'Warning, signing credential not found: ' + project.property("app.signing")+'.gradle'
}
buildTypes {
all {
buildConfigField 'String', 'IP', '"192.168.1.1"'
buildConfigField 'String', 'RTSP_IP', '"rtsp://"+IP+"/"'
//debugging
buildConfigField 'boolean', 'DEBUG_DETAILED', 'false'
buildConfigField 'boolean', 'DEBUG_UI_STATE', 'false'
buildConfigField 'boolean', 'INTERNAL_DEBUG', 'false'
buildConfigField 'boolean', 'ENABLE_VIEWSERVER', 'false'
buildConfigField 'boolean', 'INJECT_PTP_PROPERTIES', 'false'
//functional
buildConfigField 'boolean', 'ENABLE_TIME_LIMIT', 'false'
buildConfigField 'boolean', 'HIDE_ACTIONBAR_ON_LANDSCAPE', 'false'
buildConfigField 'boolean', 'ENABLE_VIDEO_DOWNLOAD', 'true'
buildConfigField 'boolean', 'ENABLE_VIDEO_DOWNLOAD_PROGRESS', 'true'
buildConfigField 'boolean', 'ENABLE_VIDEO_DOWNLOAD_CANCEL', 'false'
buildConfigField 'boolean', 'SET_TIME', 'true'
buildConfigField 'boolean', 'SHOULD_SET_CAMERA_MODE_WHEN_TURNING_RECORDING_OFF', 'false'
buildConfigField 'boolean', 'SHOULD_SET_CAMERA_MODE_ON_CONNECTION', 'false'
appendVersionNameVersionCode(applicationVariants)
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
//for customers' testing
debug {
buildConfigField 'boolean', 'ENABLE_TIME_LIMIT', 'true'
}
//for internal testing
debugEx {
buildConfigField 'boolean', 'DEBUG_DETAILED', 'true'
buildConfigField 'boolean', 'INTERNAL_DEBUG', 'true'
buildConfigField 'boolean', 'ENABLE_VIEWSERVER', 'true'
buildConfigField 'boolean', 'INJECT_TEST_PROPERTIES', 'true'
debuggable true
signingConfig signingConfigs.debug
applicationIdSuffix ".debug"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
def time=Calendar.getInstance()
time.add(Calendar.MONTH, 3)
println 'Debug build expiry date='+time.getTime()
productFlavors {
// default BuildConfig variables
all {
buildConfigField 'long', 'TIME_LIMIT', time.getTimeInMillis()+'l'
buildConfigField 'boolean', 'ADD_ABOUT', 'true'
buildConfigField 'boolean', 'FORCE_DEVICE_CHECK', 'false'
buildConfigField 'boolean', 'SHOW_CUR_SELECTION_PREF', 'true'
buildConfigField 'boolean', 'SHOW_CUR_SELECTION_ONSCREEN', 'false'
buildConfigField 'boolean', 'NO_WIFI_SCREEN', 'true'
buildConfigField 'boolean', 'NO_STREAMING', 'false'
buildConfigField 'boolean', 'NO_GALLERY', 'false'
buildConfigField 'boolean', 'INIT_IN_START', 'true'
buildConfigField 'boolean', 'CUSTOM_FUNCTIONS', 'true'
buildConfigField 'boolean', 'ENABLE_TIMEOUT_CONTINUE', 'false'
buildConfigField 'boolean', 'TRANSPARENT_BOTTOM_BAR', 'false'
buildConfigField 'int', 'LOGO_TIMING', '1000'
}
default {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0xFF'
buildConfigField 'boolean', 'ADD_ABOUT', 'false'
applicationId = 'com.app.default'
def variantName='DEFAULT'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
Customer_1 {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0x0B'
buildConfigField 'boolean', 'FORCE_DEVICE_CHECK', 'true'
applicationId 'com.app.c1'
def variantName='c1'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
Customer_2 {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0xFF' //TODO not final
buildConfigField 'boolean', 'SHOW_CUR_SELECTION_ONSCREEN', 'true'
applicationId 'com.app.c2'
def variantName='c2'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
Customer_3 {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0x12'
buildConfigField 'int', 'LOGO_TIMING', '3000'
applicationId = 'com.app.c3'
def variantName='c3'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
Customer_4 {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0x02'
applicationId = 'com.app.c4'
def variantName='c4'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
Customer_5 {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0x04'
applicationId = 'com.app.c5'
def variantName='c5'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
Customer_6 {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0xFF'
applicationId = 'com.app.c6'
def variantName='c6'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
Customer_7 {
//mandatory
buildConfigField 'int', 'CUSTOMER_NAME_PREFIX', '0x14'
buildConfigField 'boolean', 'FORCE_DEVICE_CHECK', 'true'
applicationId = 'com.app.c7'
def variantName='c7'
versionCode retrieveVersionCode(variantName)
versionName retrieveVersionName(variantName)
}
}
sourceSets{
main {
res.srcDirs = ['src/main/res']
}
default {
res.srcDir 'src/_Strings_/Standard'
res.srcDir 'src/_Strings_/xx'
}
Customer_1 {
res.srcDir 'src/_Strings_/Standard'
res.srcDir 'src/_Strings_/xx'
}
Customer_2 {
res.srcDir 'src/_Strings_/Standard'
res.srcDir 'src/_Strings_/xx'
}
Customer_3 {
res.srcDir 'src/_Strings_/Standard'
res.srcDir 'src/_Strings_/xx'
res.srcDir 'src/_Strings_/yy'
}
Customer_4 {
res.srcDir 'src/_Strings_/Standard'
res.srcDir 'src/_Strings_/xx'
}
Customer_5 {
res.srcDir 'src/_Strings_/xx'
res.srcDir 'src/_Strings_/zz'
}
Customer_6 {
res.srcDir 'src/_Strings_/xx'
res.srcDir 'src/_Strings_/aa'
}
Customer_7 {
res.srcDir 'src/_Strings_/Standard'
res.srcDir 'src/_Strings_/xx'
}
}
}
import java.util.regex.Pattern
def variantNameRegex = Pattern.quote("generate") + "(.*?)"+ Pattern.quote("BuildConfig")
Pattern patternVariantName = Pattern.compile(variantNameRegex);
tasks.whenTaskAdded { task ->
//TODO disables lint
if (task.name.startsWith("lint")) {
println 'Disables lint task: '+task.name
task.enabled = false
}
def m = patternVariantName.matcher(task.name)
if (m.find()) {
def variantName = m.group(1)
def isRelease=false
if (variantName.endsWith('Debug')) {
variantName = variantName.substring(0, variantName.lastIndexOf('Debug'))
} else if (variantName.endsWith('Release')) {
variantName = variantName.substring(0, variantName.lastIndexOf('Release'))
isRelease=true;
} else {
return
}
def taskIncVerCode="increaseVersionCode$variantName"
if(!project.hasProperty(taskIncVerCode)) {
project.task(taskIncVerCode) << {
def manifestFile = file("src/$variantName/AndroidManifest.xml")
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def manifestText = manifestFile.getText()
def matcher = pattern.matcher(manifestText)
matcher.find()
def versionCode = Integer.parseInt(matcher.group(1))
def manifestContent = matcher.replaceAll("versionCode=\"" + ++versionCode + "\"")
manifestFile.write(manifestContent)
}
}
task.dependsOn taskIncVerCode
if(isRelease) {
def taskIncVerName="increaseVersionName$variantName"
if(!project.hasProperty(taskIncVerName)) {
project.task(taskIncVerName) << {
def manifestFile = file("src/$variantName/AndroidManifest.xml")
def patternVersionNumber = Pattern.compile("versionName=\"(\\d+)\\.(\\d+)\\.(\\d+)\"")
def manifestText = manifestFile.getText()
def matcherVersionNumber = patternVersionNumber.matcher(manifestText)
matcherVersionNumber.find()
def majorVersion = Integer.parseInt(matcherVersionNumber.group(1))
def minorVersion = Integer.parseInt(matcherVersionNumber.group(2))
def pointVersion = Integer.parseInt(matcherVersionNumber.group(3))
def mNextVersionName = majorVersion + "." + minorVersion + "." + (pointVersion + 1)
def manifestContent = matcherVersionNumber.replaceAll("versionName=\"" + mNextVersionName + "\"")
manifestFile.write(manifestContent)
}
}
task.dependsOn taskIncVerName
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:support-v4:21.0.0'
compile files('libs/eventbus.jar')
compile files('libs/libGoogleAnalyticsServices.jar')
compile files('libs/trove-3.0.3.jar')
}
Здесь вывод консоли gradle, сгенерированный при выполнении Run дважды без изменений gralde/code:
Executing tasks: [:ptp_app_base:assembleCustomer_6DebugEx]
Parallel execution with configuration on demand is an incubating feature.
Debug build expiry date=Mon Mar 16 10:39:02 CST 2015
Disables lint task: lintVitalCustomer_1Release
Disables lint task: lintVitalCustomer_2Release
Disables lint task: lintVitalDefaultRelease
Disables lint task: lintVitalCustomer_3Release
Disables lint task: lintVitalCustomer_4Release
Disables lint task: lintVitalCustomer_5Release
Disables lint task: lintVitalCustomer_6Release
Disables lint task: lintVitalCustomer_7Release
Disables lint task: lint
Disables lint task: lintCustomer_1DebugEx
Disables lint task: lintCustomer_1Debug
Disables lint task: lintCustomer_1Release
Disables lint task: lintCustomer_2DebugEx
Disables lint task: lintCustomer_2Debug
Disables lint task: lintCustomer_2Release
Disables lint task: lintDefaultDebugEx
Disables lint task: lintDefaultDebug
Disables lint task: lintDefaultRelease
Disables lint task: lintCustomer_3DebugEx
Disables lint task: lintCustomer_3Debug
Disables lint task: lintCustomer_3Release
Disables lint task: lintCustomer_4DebugEx
Disables lint task: lintCustomer_4Debug
Disables lint task: lintCustomer_4Release
Disables lint task: lintCustomer_5DebugEx
Disables lint task: lintCustomer_5Debug
Disables lint task: lintCustomer_5Release
Disables lint task: lintCustomer_6DebugEx
Disables lint task: lintCustomer_6Debug
Disables lint task: lintCustomer_6Release
Disables lint task: lintCustomer_7DebugEx
Disables lint task: lintCustomer_7Debug
Disables lint task: lintCustomer_7Release
ptp_app_base-Customer_1-debugEx.apk --> MY_APP_Customer_1_debugEx_.apk
ptp_app_base-Customer_1-debug.apk --> MY_APP_Customer_1_20141216.apk
ptp_app_base-Customer_1-release.apk --> MY_APP_Customer_1_20141216_SIGNED.apk
ptp_app_base-Customer_2-debugEx.apk --> MY_APP_Customer_2_debugEx_.apk
ptp_app_base-Customer_2-debug.apk --> MY_APP_Customer_2_20141216.apk
ptp_app_base-Customer_2-release.apk --> MY_APP_Customer_2_20141216_SIGNED.apk
ptp_app_base-default-debugEx.apk --> MY_APP_default_debugEx_.apk
ptp_app_base-default-debug.apk --> MY_APP_default_20141216.apk
ptp_app_base-default-release.apk --> MY_APP_default_20141216_SIGNED.apk
ptp_app_base-Customer_3-debugEx.apk --> MY_APP_Customer_3_debugEx_.apk
ptp_app_base-Customer_3-debug.apk --> MY_APP_Customer_3_20141216.apk
ptp_app_base-Customer_3-release.apk --> MY_APP_Customer_3_20141216_SIGNED.apk
ptp_app_base-Customer_4-debugEx.apk --> MY_APP_Customer_4_debugEx_.apk
ptp_app_base-Customer_4-debug.apk --> MY_APP_Customer_4_20141216.apk
ptp_app_base-Customer_4-release.apk --> MY_APP_Customer_4_20141216_SIGNED.apk
ptp_app_base-i3-debugEx.apk --> MY_APP_i3_debugEx_.apk
ptp_app_base-i3-debug.apk --> MY_APP_i3_20141216.apk
ptp_app_base-i3-release.apk --> MY_APP_i3_20141216_SIGNED.apk
ptp_app_base-i5-debugEx.apk --> MY_APP_i5_debugEx_.apk
ptp_app_base-i5-debug.apk --> MY_APP_i5_20141216.apk
ptp_app_base-i5-release.apk --> MY_APP_i5_20141216_SIGNED.apk
ptp_app_base-Customer_7-debugEx.apk --> MY_APP_Customer_7_debugEx_.apk
ptp_app_base-Customer_7-debug.apk --> MY_APP_Customer_7_20141216.apk
ptp_app_base-Customer_7-release.apk --> MY_APP_Customer_7_20141216_SIGNED.apk
:ptp_app_base:preBuild
:ptp_app_base:compileCustomer_6DebugExNdk UP-TO-DATE
:ptp_app_base:preCustomer_6DebugExBuild
:ptp_app_base:checkCustomer_6DebugExManifest
:ptp_app_base:preCustomer_4DebugBuild
:ptp_app_base:preCustomer_4DebugExBuild
:ptp_app_base:preCustomer_4ReleaseBuild
:ptp_app_base:preCustomer_5DebugBuild
:ptp_app_base:preCustomer_5DebugExBuild
:ptp_app_base:preCustomer_5ReleaseBuild
:ptp_app_base:preCustomer_6DebugBuild
:ptp_app_base:preCustomer_6ReleaseBuild
:ptp_app_base:preDefaultDebugBuild
:ptp_app_base:preDefaultDebugExBuild
:ptp_app_base:preDefaultReleaseBuild
:ptp_app_base:preCustomer_3DebugBuild
:ptp_app_base:preCustomer_3DebugExBuild
:ptp_app_base:preCustomer_3ReleaseBuild
:ptp_app_base:preCustomer_7DebugBuild
:ptp_app_base:preCustomer_7DebugExBuild
:ptp_app_base:preCustomer_7ReleaseBuild
:ptp_app_base:preCustomer_1DebugBuild
:ptp_app_base:preCustomer_1DebugExBuild
:ptp_app_base:preCustomer_1ReleaseBuild
:ptp_app_base:preCustomer_2DebugBuild
:ptp_app_base:preCustomer_2DebugExBuild
:ptp_app_base:preCustomer_2ReleaseBuild
:ptp_app_base:prepareComAndroidSupportSupportV42100Library UP-TO-DATE
:ptp_app_base:prepareCustomer_6DebugExDependencies
:ptp_app_base:compileCustomer_6DebugExAidl UP-TO-DATE
:ptp_app_base:compileCustomer_6DebugExRenderscript UP-TO-DATE
:ptp_app_base:generateCustomer_6DebugExBuildConfig
:ptp_app_base:generateCustomer_6DebugExAssets UP-TO-DATE
:ptp_app_base:mergeCustomer_6DebugExAssets UP-TO-DATE
:ptp_app_base:generateCustomer_6DebugExResValues UP-TO-DATE
:ptp_app_base:generateCustomer_6DebugExResources UP-TO-DATE
:ptp_app_base:mergeCustomer_6DebugExResources UP-TO-DATE
:ptp_app_base:processCustomer_6DebugExManifest UP-TO-DATE
:ptp_app_base:processCustomer_6DebugExResources UP-TO-DATE
:ptp_app_base:generateCustomer_6DebugExSources
:ptp_app_base:compileCustomer_6DebugExJava
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
:ptp_app_base:preDexCustomer_6DebugEx UP-TO-DATE
:ptp_app_base:dexCustomer_6DebugEx
:ptp_app_base:processCustomer_6DebugExJavaRes UP-TO-DATE
:ptp_app_base:validateDebugSigning
:ptp_app_base:packageCustomer_6DebugEx
:ptp_app_base:zipalignCustomer_6DebugEx
:ptp_app_base:assembleCustomer_6DebugEx
BUILD SUCCESSFUL
Total time: 30.303 secs
Текущая сборка script, вероятно, не самая результативная, поэтому будут рассмотрены как советы о том, как пропустить пересоздание, так и ускорить перестройку.
Изменить 2: Я заметил, что большая часть времени сборки gradle расходуется на:
- скомпилировать [app] Java
- Dex [приложение]
- пакет [приложение]
Эти шаги появляются, несмотря на то, что с момента последней сборки ничего не изменилось.
Изменить 3: Оригинальное название: "Как пропустить gradle сборку при запуске/отладке с помощью Android Studio", измененный, чтобы лучше отражать симптом проблемы и средства защиты.