我想有我的Gradle构建创建一个发布签名apk文件使用Gradle。

我不确定代码是否正确,或者我在做gradle构建时是否缺少一个参数?

这是build.gradle/build.gradle中的一些代码。节文件:

android {
    ...
    signingConfigs {
        release {
            storeFile(file("release.keystore"))
            storePassword("******")
            keyAlias("******")
            keyPassword("******")
        }
    }
}

Gradle构建成功完成,在我的build/apk文件夹中,我只看到…-release-unsigned.apk和…-debug-unaligned.apk文件。

对如何解决这个问题有什么建议吗?


当前回答

@Destil的答案很好,如果你可以在所有项目中重用相同的配置。或者,Android Studio自带本地文件。可以使用的属性文件,但它应该是ide生成的,我找不到一种方法来扩展它从Android Studio。

这是@jonbo回答的一个变体。这个答案允许项目特定的设置,但它带来了一些开发人员的开销。具体来说,需要大量的样板文件来将signingConfigs定义移动到一个单独的文件中——特别是当您需要为多个项目这样做时,这是选择这个解决方案而不是Destil的主要原因。这可以通过包含线条在一定程度上得到缓解

apply plugin: 'com.android.application'

在凭证文件中,因为这将允许IDE完成。

最后,这里的大多数解决方案不允许在不提供语法上(如果不是语义上)有效的signingConfigs定义的情况下以调试模式构建项目(自动处理调试签名)。如果您不需要从给定的机器生成一个发布版本,那么这个额外的步骤可以被视为一个不必要的障碍。另一方面,它可以帮助在生产中运行调试构建的无知或懒惰的同事。

这个解决方案将允许调试构建而完全不用担心凭证,但是它将需要有效的凭证来生成发布构建,并且它只需要很少的样板文件。然而,作为一个缺点,它可能会鼓励其他人用真实的凭证替换虚拟值,并且没有办法防止这种情况。

// app/build.gradle
// Define this structure in signing.gradle to enable release builds.
ext.signing = [
        storeFilePath : 'path/to/keystore',
        storePassword : 'keystore password',
        keyAlias      : 'key alias',
        keyPassword   : 'key password',
]

if (file('signing.gradle').exists()) {
    apply from: 'signing.gradle'
}

android {
    ...
    signingConfigs {
        release {
            storeFile file(project.signing.storeFilePath)
            storePassword project.signing.storePassword
            keyAlias project.signing.keyAlias
            keyPassword project.signing.keyPassword
        }
    }
    buildTypes {
        debug { ... }
        release {
            signingConfig signingConfigs.release
            ...
        }
    }
}

这将创建一个虚拟属性,纯粹用于生成语法上有效的构建文件。在调试构建阶段,分配给ext.signing属性的值是不相关的。要启用发布版本,请将ext.signing复制到signing中。Gradle并将虚拟值替换为有效凭证。

// signing.gradle
ext.signing = [
        storeFilePath : 'real/keystore',
        storePassword : 'real keystore password',
        keyAlias : 'real key alias',
        keyPassword : 'real key password',
]

当然,签名。gradle应该被VCS忽略。

其他回答

这是解决同一问题的另一种方法。由于不建议在源代码中存储任何类型的凭据,我们决定在一个单独的属性文件中设置密钥存储和密钥别名的密码,如下所示:

key.store.password=[STORE PASSWORD]
key.alias.password=[KEY PASSWORD]

如果使用git,可以创建一个名为secure.properties的文本文件。您应该确保从存储库中排除它(如果使用git,则将其添加到.gitignore文件中)。然后,您需要创建一个签名配置,就像其他一些答案所指出的那样。唯一的区别是如何加载凭据:

android {
    ...
    signingConfigs {
        ...
        release {
            storeFile file('[PATH TO]/your_keystore_file.jks')
            keyAlias "your_key_alias"

            File propsFile = file("[PATH TO]/secure.properties");
            if (propsFile.exists()) {
                Properties props = new Properties();
                props.load(new FileInputStream(propsFile))
                storePassword props.getProperty('key.store.password')
                keyPassword props.getProperty('key.alias.password')
            }
        }
        ...
    }

    buildTypes {
        ...
        release {
            signingConfig signingConfigs.release
            runProguard true
            proguardFile file('proguard-rules.txt')
        }
        ...
    }
}

永远不要忘记手动将signingConfig分配给发行版构建类型(出于某些原因,我有时假设它将被自动使用)。此外,启用proguard并不是强制性的,但建议这样做。

与使用环境变量或请求用户输入相比,我们更喜欢这种方法,因为它可以从IDE中完成,通过切换到release构建类型并运行应用程序,而不必使用命令行。

注意@sdqali的脚本会(至少在使用Gradle 1.6时)要求输入密码 任何时候调用gradle任务。因为你只在做gradle assemblerrelease(或类似)时需要它,你可以使用下面的技巧:

android {
    ...
    signingConfigs {
        release {
            // We can leave these in environment variables
            storeFile file(System.getenv("KEYSTORE"))
            keyAlias System.getenv("KEY_ALIAS")

            // These two lines make gradle believe that the signingConfigs
            // section is complete. Without them, tasks like installRelease
            // will not be available!
            storePassword "notYourRealPassword"
            keyPassword "notYourRealPassword"
        }
    }
    ...
}

task askForPasswords << {
    // Must create String because System.readPassword() returns char[]
    // (and assigning that below fails silently)
    def storePw = new String(System.console().readPassword("Keystore password: "))
    def keyPw  = new String(System.console().readPassword("Key password: "))

    android.signingConfigs.release.storePassword = storePw
    android.signingConfigs.release.keyPassword = keyPw
}

tasks.whenTaskAdded { theTask -> 
    if (theTask.name.equals("packageRelease")) {
        theTask.dependsOn "askForPasswords"
    }
}

注意,我还必须添加以下(在android下)使其工作:

buildTypes {
    release {
        signingConfig signingConfigs.release
    }
}

使用git时使用Gradle自动应用程序签名

令人惊讶的是,有这么多复杂的方法可以做到这一点。这是我自己的方法,我试图坚持谷歌自己的建议。但是,他们的解释并不完全清楚,所以我将详细描述Linux的过程。


描述:

自动签名应用程序的默认谷歌指令 在构建过程中,不保留密码和签名文件 在你的应用程序开发(GIT)路径中,是相当模糊的。这里是 详细说明了如何做到这一点。

最初的假设:

你有一个名为“MyApp”的应用程序,在以下路径给出的目录中: $ HOME / / mydev / MyApp项目。但是,使用MyApp目录和 由GIT控制。

问题

我们显然不希望在任何地方有我们的签名或密码文件 GIT控制的目录,即使我们很会使用。gitignore等,它仍然太冒险,容易犯错误。因此,我们希望我们的密钥存储库和签名文件在外部。

解决方案

我们需要做三件事:

创建一个Android Studio使用的密码文件 创建签名密钥文件 编辑模块构建。Gradle文件使用(1)和(2)。

在本例中,我们将这两个文件命名为:

keystore.properties MyApp-release-key.jks

我们可以把这两个文件放在这里:

cd $HOME/projects/mydev/

(1)创建keystore密码文件

第一个文件包含明文密码;和(2)中释放密钥文件的路径。从填写这个开始,因为它将使下一步的复制粘贴操作更容易。

cd $HOME/projects/mydev/

编辑密钥存储库。属性,所以它的内容是:

storePassword=myStorePassword
keyPassword=mykeyPassword
keyAlias=myKeyAlias
storeFile=myStoreFileLocation

这里唯一棘手的部分是myStoreFileLocation。这是从模块构建中看到的路径。Gradle文件。这通常意味着路径类似于:$HOME/projects/mydev/MyApp/app/build.gradle。为了指向myapp -release-key。jks 文件,我们需要在这里放的是:

. . / . . / . . / MyApp-release-key.jks

这里,我们还为键选择了“myapp”别名。然后最终文件应该是这样的:

storePassword=myStorePassword
keyPassword=mykeyPassword
keyAlias=myapp
storeFile=../../../MyApp-release-key.jks

(2)创建签名文件

第二个文件在创建签名密钥时自动生成。 如果你没有其他应用程序,这是你唯一的密钥库,然后创建文件:

cd $HOME/projects/mydev/
keytool -genkeypair -v -keystore MyApp-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias myapp

这将要求您输入两个密码和一堆信息。(与Android Studio相同。)现在复制/粘贴您之前选择的密码。

(3)编辑gradle模块。构建文件以使用上面的内容

以下部分需要出现在你的应用/模块的Gradle构建文件中。首先,在android{}块的外面和前面添加以下行。

//def keystorePropertiesFile = rootProject.file("$HOME/.android/keystore.properties")
def keystorePropertiesFile = rootProject.file("../../keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

然后,在android{}块中添加:

android {
    ...
    defaultConfig { ... }
    signingConfigs {
            release {
                keyAlias keystoreProperties['keyAlias']
                keyPassword keystoreProperties['keyPassword']
                storeFile file(keystoreProperties['storeFile'])
                storePassword keystoreProperties['storePassword']
            }
        }
    // Tell Gradle to sign your APK
    buildTypes {
        release {
            signingConfig signingConfigs.release
            ...
        }
    }
}

现在从shell,你可以重建你的应用程序:

cd $HOME/projects/mydev/MyApp/app/
./gradlew clean build

这应该生成一个正确签名的应用程序,可以在谷歌播放中使用。


更新:2019-04-02

最新版本的keytool和一些东西告诉你,你应该使用一个基于PKCS12的keyfile,而不是我上面使用的原始/默认的keyfile。然后他们继续告诉你,你应该转换到新的开放PKCS12格式。然而,似乎Android开发工具还没有完全准备好,因为如果你这样做了,你会得到以下奇怪的错误:

com.android.ide.common.signing.KeytoolException: read key失败 “F:\XXX\XXX.”获取键失败:给定的最终块没有 适当的衬垫。如果在过程中使用了错误的密钥,就会出现这种问题 解密。

所以不要使用转换后的密钥!

Android工作室 转到文件->项目结构或按Ctrl+Alt+Shift+S

查看图片

单击Ok

然后在构建中生成signingconfig。gradle文件。

扩展David Vavra的回答,创建一个文件~/.gradle/gradle。属性和添加

RELEASE_STORE_FILE=/path/to/.keystore
RELEASE_KEY_ALIAS=XXXXX
RELEASE_STORE_PASSWORD=XXXXXXXXX
RELEASE_KEY_PASSWORD=XXXXXXXXX

然后在build.gradle中

  signingConfigs {
    release {
    }
  }

  buildTypes {
    release {
      minifyEnabled true
      shrinkResources true

    }
  }

  // make this optional
  if ( project.hasProperty("RELEASE_KEY_ALIAS") ) {
    signingConfigs {
      release {
        storeFile file(RELEASE_STORE_FILE)
        storePassword RELEASE_STORE_PASSWORD
        keyAlias RELEASE_KEY_ALIAS
        keyPassword RELEASE_KEY_PASSWORD
      }
    }
    buildTypes {
      release {
        signingConfig signingConfigs.release
      }
    }
  }