1. 程式人生 > >Android studio之簽名配置

Android studio之簽名配置

在上一篇教程中,我講解了Android studio多渠道打包的相關配置,在這一篇部落格中,我們將繼續講解Android studio的簽名配置。如果還沒有看過上一篇的朋友,或者還不瞭解多渠道打包的,可以先看這一篇部落格

Let’s go,讓我們開始新的教程。
我們可以新建一個專案,然後生成一個新的簽名檔案。
一般來說,經過這些操作之後,我們便可以通過點選左上角的Build->Generate Signed APK->Next,然後選擇對應的Build Type進行打包。

那對一些人來說,這樣也太麻煩了,每次都得輸入相關資訊,還得進行選擇,這怎麼能忍!
那麼有更簡單快捷的方法嗎?答案是有的。

我們可以在專案的app目錄下的build.gradle中進行簽名的配置。
首先我們找到build.gradle下的android{},預設配置如下所示

這裡寫圖片描述

接下來呢,我們可以把專案的預覽模式調成Project,不調也行,只需把剛才生成的簽名檔案放至到對應的目錄下即可。我的做法是直接放至到專案下,如圖所示

這裡寫圖片描述

然後,回到app下的build.gradle,在裡面增加這段程式碼

signingConfigs {
        debug {
            storeFile file('../test.jks')//簽名檔案路徑
            storePassword "123456"
keyAlias "test" keyPassword "123456" //簽名密碼 println("====== signingConfigs.debug ======") } release { storeFile file('../test.jks')//簽名檔案路徑 storePassword "123456" keyAlias "test" keyPassword "123456" //簽名密碼
println("====== signingConfigs.release ======") } }

我來解釋一下,storeFile file對應的就是簽名檔案key的路徑,我們的簽名放在專案的直接子路徑下,而build.gradle在test/app下,所以我們要用 .. 找到專案的根路徑,再通過 / 找到對應的簽名檔案所在路徑。當然,你也可以自己配置簽名檔案的地址,只要能找到即可。

這裡寫圖片描述

此時,我們在專案路徑下的app/build/outputs可以看到我們打包的apk,從名字可以看出,該APK是已經簽名的。那麼就是說,用程式碼配置,可以達到和使用圖形介面一樣的效果。

對了,這裡有一點需要注意,就是signingConfigs程式碼塊一定要寫在buildTypes前面,否則會報下面這種錯: Could not find property ‘debugConfig’ on SigningConfig container.

至此,我們的buildTypes的配置可以這樣:

buildTypes {
        debug {
            println("====== buildTypes.debug ======")
            signingConfig signingConfigs.debug
        }
        release {
            //是否混淆
            minifyEnabled false
            //是否移除無用資源
            zipAlignEnabled true
            println("====== buildTypes.release ======")
            signingConfig signingConfigs.release
            //混淆的配置檔案
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

大家現在便可以通過命令列進行任意操縱,或者進行多渠道打包。
不過,上述的配置雖然配置簡單,但是存在不安全性,假如你的專案是開源的,你把簽名檔案的配置密碼之類的資訊用明文寫在build.gradle裡面,那是不是很不安全呢?

所以,我們可以這麼做。在專案路徑下新建一個.properties的檔案,或者直接local.properties下直接新增相關資訊即可。

我們可以直接在該檔案下新增:(填寫相關資訊)

keystore.path=../test.jks              //替換成自己的簽名路徑
keystore.password=123456        
keystore.alias=test
keystore.alias_password=123456

那麼,在build.gradle下面,為了不用明文顯示,我們首先要獲得key的相關配置,所以我們可以在app的build.gradle下面新增該程式碼

def keystoreFilepath = ''
def keystorePSW = ''
def keystoreAlias = ''
def keystoreAliasPSW = ''
// default keystore file, PLZ config file path in local.properties
def keyfile = file('s.keystore.temp')

Properties properties = new Properties()
// local.properties file in the root director
properties.load(project.rootProject.file('local.properties').newDataInputStream())
keystoreFilepath = properties.getProperty("keystore.path")

if (keystoreFilepath) {
    keystorePSW = properties.getProperty("keystore.password")
    keystoreAlias = properties.getProperty("keystore.alias")
    keystoreAliasPSW = properties.getProperty("keystore.alias_password")
    keyfile = file(keystoreFilepath)
}

首先,便是給key賦預設值,然後根據Properties的配置檔案,然後根據我們在local.properties下的配置引數keystore.password等獲取簽名檔案的配置資訊。

這裡有一點需要強調,在git版本控制的專案中,我們可以看到我們專案下有一個.gitignore的檔案,裡面的配置大概如下所示

這裡寫圖片描述

我們可以看到/local.properties,意思就是說local.properties預設是不新增到版本控制裡面的,因為local.properties儲存的是我們環境資源的一些相關資訊,如sdk的路徑。故我們可以在local.properties下配置簽名信息而不用擔心金鑰外洩。對於開源專案來說,是非常好的。

那麼,此時此刻,我們app/build.gradle下的signingConfigs可以改為:

signingConfigs {
        debug {
            storeFile keyfile
            storePassword keystorePSW
            keyAlias keystoreAlias
            keyPassword keystoreAliasPSW
            println("====== signingConfigs.debug ======")
        }
        myConfig {
            storeFile keyfile
            storePassword keystorePSW
            keyAlias keystoreAlias
            keyPassword keystoreAliasPSW
            println("====== signingConfigs.release ======")
        }
 }

相應的,buildTypes也可以配置成這樣

buildTypes {
        debug {
            println("====== buildTypes.debug ======")
            signingConfig signingConfigs.debug
        }
        release {
            //是否混淆
            minifyEnabled false
            //是否移除無用資源
            zipAlignEnabled true
            //混淆配置
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            //簽名檔案存在,則簽名
            if (keyfile.exists()) {
                println("WITH -> buildTypes -> release: using jks key")
                signingConfig signingConfigs.myConfig
            }else {
                println("WITH -> buildTypes -> release: using default key")
            }
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        // 輸出apk名稱為ruijie_v1.0_wandoujia.apk
                        def fileName = "ruijie_v${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }

這樣的話,當我們的專案是開源的,那麼我們不會上傳簽名檔案,我們通過上述程式碼keyfile.exists()進行判斷是否有簽名,那麼其他人打包出來的apk是未簽名的。所以能夠保證apk簽名的安全性。

附加一下:build.gradle的相關配置:

有時候我們打包會顯示lint報錯,那麼我們可以新增

    lintOptions {
        abortOnError false
    }

dx工具(android將jar包轉成dex格式二進位制jar包工具)
這將應用所有使用dex的task。解決65535的配置

dexOptions {
        incremental true
        javaMaxHeapSize "4g"
        //關閉預編譯
        preDexLibraries = false
    }

或者指定JDK的編譯版本

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

打包忽略掉第三方jar相同的檔案

 packagingOptions {
        exclude 'META-INF/DEPENDENCIES.txt'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/dependencies.txt'
        exclude 'META-INF/LGPL2.1'
    }

最後,附上build.gradle部分配置的相關程式碼:

apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'android-apt'

def keystoreFilepath = ''
def keystorePSW = ''
def keystoreAlias = ''
def keystoreAliasPSW = ''
// default keystore file, PLZ config file path in local.properties
def keyfile = file('s.keystore.temp')

Properties properties = new Properties()
// local.properties file in the root director
properties.load(project.rootProject.file('local.properties').newDataInputStream())
keystoreFilepath = properties.getProperty("keystore.path")

if (keystoreFilepath) {
    keystorePSW = properties.getProperty("keystore.password")
    keystoreAlias = properties.getProperty("keystore.alias")
    keystoreAliasPSW = properties.getProperty("keystore.alias_password")
    keyfile = file(keystoreFilepath)
}

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        applicationId "com.example.vizax.with"
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // 這個是解決lint報錯的程式碼
    lintOptions {
        abortOnError false
    }

    dexOptions {
        incremental true
        javaMaxHeapSize "4g"
        //關閉預編譯
        preDexLibraries = false
    }
    /*android {
        productFlavors {
            kuan {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "kuan"]
            }
            xiaomi {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
            }
            qh360 {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qh360"]
            }
            baidu {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
            }
            wandoujia {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
            }
        }
    }*/
    android {
        productFlavors {
            kuan {}
            xiaomi {}
            qh360 {}
            baidu {}
            wandoujia {}
            yingyongbao {}
        }

        productFlavors.all {
            flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }
    }

    signingConfigs {
        debug {
            storeFile keyfile
            storePassword keystorePSW
            keyAlias keystoreAlias
            keyPassword keystoreAliasPSW
            println("====== signingConfigs.debug ======")
        }
        myConfig {
            storeFile keyfile
            storePassword keystorePSW
            keyAlias keystoreAlias
            keyPassword keystoreAliasPSW
            println("====== signingConfigs.release ======")
        }
        /*release {
            storeFile file('../with.jks') //簽名檔案路徑
            storePassword "with123456"
            keyAlias "with"
            keyPassword "with123456"  //簽名密碼
        }*/
    }

    //打包忽略掉第三方jar相同的檔案
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES.txt'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/dependencies.txt'
        exclude 'META-INF/LGPL2.1'
    }

    /*signingConfigs程式碼塊一定要寫在buildTypes前面,否則會報下面這種錯:
    Could not find property 'debugConfig' on SigningConfig container.
    簽名密碼寫在gradle中不安全,故最好在打包上線的時候將相關程式碼註釋掉。*/
    buildTypes {
        debug {
            println("====== buildTypes.debug ======")
            signingConfig signingConfigs.debug
        }
        release {
            //是否混淆
            minifyEnabled false
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            if (keyfile.exists()) {
                println("WITH -> buildTypes -> release: using jks key")
                signingConfig signingConfigs.myConfig
            }else {
                println("WITH -> buildTypes -> release: using default key")
            }
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        // 輸出apk名稱為ruijie_v1.0_wandoujia.apk
                        def fileName = "ruijie_v${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }
    }

    /*  左下角開啟Terminal,輸入命令列
        如果我們想打包wandoujia渠道的release版本,執行如下命令就好了:
        gradlew assembleWandoujiaRelease
        如果我們想打包wandoujia渠道的debug版本,執行如下命令就好了:
        gradlew assembleWandoujiaDebug
        如果我們只打wandoujia渠道版本,則:
        gradlew assembleWandoujia
        此命令會生成wandoujia渠道的Release和Debug版本
        同理我想打全部Release版本:
        gradlew assembleRelease*/
}

至於混淆的相關配置,稍後我在下一篇部落格進行詳細講解。
如果有任何疑問,歡迎留言。謝謝。