1. 程式人生 > >使用JavaWeb實現Android自動化構建打包加固多渠道-GUI篇(上)

使用JavaWeb實現Android自動化構建打包加固多渠道-GUI篇(上)

一、前言:

  Android打包是一件比較繁瑣又耗時的操作,相信很多朋友都想自己搞一個屬於自己的自動化構建程式,本文則是對這一程式的實現提供一個思路方法,沒有采用 Jenkins持續整合 這種方式來實現,但大概思路都大同小異。
  關於本文需要了解的一些東西:

  1. 實現思路為:使用JavaGUI做一個zip打包工具,將自定義配置資訊寫入專案並打包成zip格式,最終將檔案拖拽上傳至伺服器,伺服器進行一系列的操作:解壓,讀取配置,打包,加固,多渠道,最後下發至瀏覽器端進行自動下載,由於公司電腦是WindowsServer2012,所以內部實現是基於Windows命令的
  2.Android專案構建配置都各有不同,所以本文主要提供一個實現思路

,感興趣的朋友可以自己動手實現一個更適合自己的構建工具
  3.專案分為GUI端和Web端,均為Java實現,大部分操作都是流/IO,字串操作,簡單易懂
  4.使用GUI端需要的配置:JDK環境變數
  5.使用Web端需要的配置:IntelliJ IDEA、Tomcat伺服器、JDK環境變數、Gradle環境變數、360加固寶PC端、AndroidSDK、Android端所使用到的所有簽名檔案
  6.開發工具,環境變數配置等本文就不多羅嗦了,畢竟不是使用說明書,但是會講一下Tomcat與IDEA配置所產生的1099埠衝突的坑,還有Gradle多配置
  7.專案地址:
     GUI端:[AutoBuildForAndroid-GUI](
https://github.com/SmartKidsLOL/AutoBuildForAndroid-GUI

     Web端:[AutoBuildForAndroid-WEB](https://github.com/SmartKidsLOL/AutoBuildForAndroid-Web

二、GUI端的實現效果圖

Image text
Image text
Image text

三、實現思路

  如圖所示,GUI端很簡單,使用也很簡單,介面也很醜(請不要在意這些細節…),因為公司的專案是加入了Tinker熱修復,考慮到對其他專案的適用性,所以目前只做了2個自定義配置,如圖所示,1是選擇打包方式是以普通Release或者Tinker熱修復包,2是輸入專案需要用到的打包簽名檔名。點選開始打包後便將這2個配置以Key-Value方式存入Properties檔案,以便伺服器以流的方式取出來存入。寫入配置後使用Zipentry壓縮流方式將專案資料夾壓縮為Zip格式輸出到指定目錄。
  專案以Mvp簡單架構實現的,打包時會簡單校驗一下是否為Android專案資料夾。

  注:自動化打包思路都是以修改並自定義配置Gradle檔案,但是前提是公司的業務及專案都可以用同樣的Gradle配置,比如三方依賴,ndk配置等主要配置都相同,但只有簽名,ApplicationID這些可變配置根據不同專案而不同,這種情況下可以將這些可變的變數配置寫入到GUI介面中,對同類型的專案進行不同的變數配置即可,最後直接將這些配置寫入到Gradle中,上傳到伺服器進行一系列操作即可。
  所以根據實際情況,我並沒有將配置寫入Gradle檔案,而是另存為Properties檔案,這種方式也是想以不動不同專案的gradle配置前提,完成自動化打包。

四、使用方式

  在你想要進行自動化打包的專案里加入以下2點:
  1.在你的Android專案根目錄下建立 keystore.propertie 檔案,加入以下內容:

KEY_PATH=D\:/tools/your.jks	// 改成你的簽名檔案地址
KEY_PASS=yourTest
ALIAS_NAME=yourTest
ALIAS_PASS=yourTest

  2.開啟app下的gradle中加入以下程式碼:

def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(rootProject.file("keystore.properties")))

android {
	...
	signingConfigs {
        release {
            try {
                storeFile file(keystoreProperties['KEY_PATH'])
                storePassword keystoreProperties['KEY_PASS']
                keyAlias keystoreProperties['ALIAS_NAME']
                keyPassword keystoreProperties['ALIAS_PASS']
            } catch (Exception ex) {
                throw new InvalidUserDataException(ex.toString())
            }
        }
    }
    
	buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
            ...
        }
    }
	...
}

  上述程式碼和Java中使用流讀取Properties檔案無異。

  Tips:如果你專案使用了Tinker,則可以自己指定基準包生成目錄,方便自定義伺服器端進行路徑配置,附上一份個人的Tinker配置Gradle檔案,最後在app下的gradle中進行apply引入,如果有需要,則可以參考

apply plugin: 'tinkerpatch-support'

/**
 * TODO: 請按自己的需求修改為適應自己工程的引數
 */
def infoPath = file("${rootDir}/infoBak")
def baseInfo = "$rootProject.ext.baseInfoName"
def variantName = "$rootProject.ext.variantName"

/**
 * 對於外掛各引數的詳細解析請參考
 * http://tinkerpatch.com/Docs/SDK
 */
tinkerpatchSupport {
    /** 可以在debug的時候關閉 tinkerPatch **/
    /** 當disable tinker的時候需要新增multiDexKeepProguard和proguardFiles,
     這些配置檔案本身由tinkerPatch的外掛自動新增,當你disable後需要手動新增
     你可以copy本示例中的proguardRules.pro和tinkerMultidexKeep.pro,
     需要你手動修改'tinker.sample.android.app'本示例的包名為你自己的包名, com.xxx字首的包名不用修改
     **/
    tinkerEnable = true
    reflectApplication = true
    /**
     * 是否開啟加固模式,只能在APK將要進行加固時使用,否則會patch失敗。
     * 如果只在某個渠道使用了加固,可使用多flavors配置
     **/
    protectedApp = true
    /**
     * 實驗功能
     * 補丁是否支援新增 Activity (新增Activity的exported屬性必須為false)
     **/
    supportComponent = true

    autoBackupApkPath = "${infoPath}"

    appKey = "$rootProject.ext.appKey"

    /** 注意: 若釋出新的全量包, appVersion一定要更新 **/
    appVersion = "$rootProject.ext.versionName"

    def pathPrefix = "${infoPath}/${baseInfo}/${variantName}/"
    def name = "${project.name}-${variantName}"

    baseApkFile = "${pathPrefix}/${name}.apk"
    baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
    baseResourceRFile = "${pathPrefix}/${name}-R.txt"
}

/**
 * 用於使用者在程式碼中判斷tinkerPatch是否被使能
 */
android {
    defaultConfig {
        buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}"
    }
}

/**
 * 一般來說,我們無需對下面的引數做任何的修改
 * 對於各引數的詳細介紹請參考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    ignoreWarning = false
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }

    res {
        pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        ignoreChange = []
        largeModSize = 100
    }

    packageConfig {
    }
    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
//        path = "/usr/local/bin/7za"
    }
    buildConfig {
        keepDexApply = false
    }
}

  最後在專案根目錄下的Gradle中配置:

ext {
// tinker配置
	versionName = "你的app versionName 如:1.0.0"
    appKey = "你的Tinker Key"
    variantName = "release"
    baseInfoName = "最後一次構建的Release基準包資料夾名"
}

  當打包時,所構建的基準包會生成在專案根目錄下的 infoBak 資料夾中,所以還需自己另行儲存最後一次基準包。並且此資料夾不會自動清除,編譯多次後會佔用非常大空間。。 所以如果不想每次手動刪除 infoBak 目錄的話,有2種方法,
  1.在自定義的 tinkerPatchGradle中加入對Debug模式的判斷,如果是Debug則不開啟Tinker功能
  2.在專案根目錄下的Gradle檔案中加入以下程式碼:

...
task clean(type: Delete) {
    delete rootProject.buildDir
    delete rootProject.file("infoBak")	// 這行是要自己加入的
}
...

五、總結

  經過GUI打包後,打出的zip包可以直接拖拽進瀏覽器開啟的伺服器地址,此外專案中會加入 AndroidToZip.properties 檔案,開啟檔案可以看到

build_pak_type=1
sign_file_name=yourKey

  build_pak_type: 0代表是普通Release包,1代表的是Tinker基準包,伺服器會根據此值來獲取不同目錄下的 app-release.apk 檔案進行打包處理。
  sign_file_name: yourKey就是你輸入的簽名檔案字首名,伺服器會根據此值來判斷使用哪一個簽名檔案進行打包處理。

  至此,GUI端功能已實現,請耐心繼續看下一篇伺服器端的實現。