AS--›Gradle 打包指定文件到jar/dex(支持java和kotlin, 支持jdk9)

藉助強大的Gradle構建系統, 可以很輕鬆的打包出jar, 再借助Android SDK中的dx.bat dx.jar文件, 就能轉換成dex

使用Gradle創建生成jar文件的任務

關於jar任務type: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html

/**
 * Jar任務類型, 不支持kotlin文件
 * */
task _makeJar(type: Jar) {
    //任務依賴於assemble任務. 所以此任務執行前, 會先執行assemble任務
    //之所以需要依賴assemble任務, 是因爲assemble執行完成後, 會把java文件轉換成class文件,
    //然後收集需要的class文件到jar文件.
    dependsOn(assemble)

    //設置生成jar文件所在的路徑
    destinationDir = file(rootDir.getAbsolutePath() + "/jar")

    //設置jar文件版本
    version = time()

    //設置jar文件中MANIFEST.MF的內容
    manifest {
        attributes("Manifest-Version": version)
    }

    //設置jar文件名稱, 名稱後面會自動拼接上 version 值.
    //具體可以查看源碼: org.gradle.api.tasks.bundling.AbstractArchiveTask#getArchiveName
    baseName "jar_file_name"

    //保存jar完整路徑到全局變量, 此變量主要用於將jar文件轉換成dex文件的參數
    project.ext.makeJarPath = getArchivePath().getAbsolutePath()

    //需要打包class文件的路徑, 這個路徑可以是任意的, 也可以是 assemble 任務生成的.
    //不同的gradle版本, 生成的路徑不一樣, 可以在 build 文件夾下搜索即可得到.
    from('/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes')
    
    //需要排除的class文件, 比如:去掉R$開頭的文件
    exclude {
        it.name.startsWith('R$') || it.name.startsWith('android')
    }

    //需要指定包含的class文件, 此規則優先於 exclude
    include {
        if (it.isDirectory()) {
            true
        } else {
            def name = it.name
            name.startsWith('Java1')
        }
    }
}

支持kt文件

task makeJar(dependsOn: ['compileReleaseJavaWithJavac'], type: Jar) {
    delete 'build/libs/kotlin_and_java.jar' //生成的jar存放路徑
    appendix = "demo"
    baseName = "androidJar"
    version = "1.0.0"
    classifier = "release"
    //後綴名
    extension = "jar"
    //最終的 Jar 包名,如果沒設置,默認爲 [baseName]-[appendix]-[version]-[classifier].[extension]
    archiveName = "kotlin_and_java.jar"
    //需打包的資源所在的路徑集
    def srcClassesDir1 = [project.buildDir.absolutePath + "/tmp/kotlin-classes/release"] //Kotlin 生成的classes文件路徑
    def srcClassesDir2 = [project.buildDir.absolutePath + "/intermediates/classes/release"] //Java 生成的classes文件路徑
    //初始化資源路徑集
    from srcClassesDir1, srcClassesDir2
    //去除路徑集下部分的資源
    exclude "**/R.class"
    exclude "**/R\$*.class"
    //只導入資源路徑集下的部分資源
    include "com/turbo/testapplication/**/*.class"
}

之後再AS IDEtasks列表就能看到_makeJar任務. 雙擊即可運行.

Jar任務Gradle官方API文檔

使用Gradle創建jar轉換dex文件的任務

/**
 * 此任務需要依賴_makeJar,因爲需要拿到jar文件路徑
 * */
task _jarToDex(type: Exec, dependsOn: _makeJar) {
    //藉助windows的cmd命令行執行
    commandLine 'cmd'

    doFirst {
        //jar文件對象
        def srcFile = file(project.ext.makeJarPath)
        //需要生成的dex文件對象
        def desFile = file(srcFile.parent + "/" + srcFile.name.substring(0, srcFile.name.lastIndexOf(".")) + ".dex")

        //此行可以不需要
        workingDir srcFile.parent

        //拼接dx.bat執行的參數
        def list = []
        list.add("/c")
        list.add("dx")
        list.add("--dex")
        list.add("--output")
        list.add(desFile)
        list.add(project.ext.makeJarPath)

        //設置參數到cmd命令行
        args list
    }
}

執行此任務前, 請確保dx.bat能在命令行中執行.

在這裏插入圖片描述

此文件在"Android SDK\build-tools\28.0.3\dx.bat", 請將Android SDK\build-tools\28.0.3\路徑添加到windowsPATH環境變量中, 即可.

Exec任務Gradle官方API文檔


2019-9-6更新

完整可用代碼如下:

/**
 * Email:[email protected]
 *
 * 腳本測試環境:
 * com.android.tools.build:gradle:3.5.0
 * gradle-5.4.1-all
 *
 * 要求:
 * 1.正確配置的`java`環境
 * 2.可直接在`命令行(cmd)`運行`dx`命令, `dx`命令配置如下:
 * 請將"{AndroidSDK}\build-tools\{任意版本}\" 路徑添加到windows的PATH環境變量中
 *
 * 注意:
 * 如果使用JDK9和以上版本, 出現以下錯誤:
 *
 * -Djava.ext.dirs=D:\android\sdk\build-tools\29.0.2\lib is not supported.  Use -classpath instead.
 * Error: Could not create the Java Virtual Machine.
 * Error: A fatal exception has occurred. Program will exit.
 *
 * 請參考: https://www.jianshu.com/p/77db2ea8098f
 *
 * @author angcyo* @date 2019/09/06
 * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
 */

ext.dexConfig = [

        //需要打包的包名(包含包名下的所有類)
        "includePackageNames": [
                "com/angcyo/uikitdemo/dex",
                "com/angcyo/uikitdemo/dex2"
        ],
        //文件名 (默認是工程名+時間的組合)
        "dexFileName"        : "",
        //當前版本
        "versionCode"        : "1",
        "versionName"        : "v1.0.0",

        //內部使用
        "jarPath"            : "",
        "dexPath"            : ""
]


task _makeDex() {
    dependsOn("jarToDex")

    doFirst {
        println "開始打包dex文件:$dexConfig.includePackageNames"
    }

    doLast {
        println "打包完成:$dexConfig.dexPath"
    }
}


/**
 * 這裏的依賴任務`compileApkReleaseJavaWithJavac`請根據"compile(.*)JavaWithJavac"匹配規則
 * 找到自己工程對應任務名.
 *
 * 此任務執行後, 纔會生成kotlin語言對應的 .class 文件.
 * */
task makeJar(dependsOn: ['compileApkReleaseJavaWithJavac'], type: Jar) {

    //需要打包進jar的包名(此包名下的所有class)
    def includePackageNames = dexConfig.includePackageNames

    //編譯類型 風味類型
    def buildFlavors = "apkRelease"
    def buildDir = project.buildDir.getAbsolutePath()

    def javaClassPath = "$buildDir/intermediates/javac/$buildFlavors/classes"
    def kotlinClassPath = "$buildDir/tmp/kotlin-classes/$buildFlavors"

    //jar 輸出目錄
    destinationDirectory.set(rootProject.file("dex"))

    //名字規則
    //[archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension]
    //jar 文件名

    if (dexConfig.dexFileName.isEmpty()) {
        archiveBaseName.set(project.name)
        def time = new Date().format("yyyy-MM-dd_HH-mm", TimeZone.getTimeZone("Asia/Shanghai"))
        archiveAppendix.set(time)
    } else {
        archiveBaseName.set(dexConfig.dexFileName)
    }

    archiveVersion.set(dexConfig.versionName)
    archiveExtension.set("jar")

    //archiveClassifier.set("all")

    //複製文件的源路徑
    from kotlinClassPath, javaClassPath
    //需要匹配的規則, 如果未設置, 則全部複製
    //include javaClassPath, kotlinClassPath

    //需要匹配的規則, 如果未設置, 則全部不排除
    //exclude

    include { fileTree ->
        includePackageNames.any {
            it.startsWith(fileTree.path)
        } || includePackageNames.any {
            fileTree.path.startsWith(it)
        }
    }

    //寫入jar文件中的 META-INF/MANIFEST.MF 文件的信息
    manifest {
        attributes["Build-Time"] = new Date().format("yyyy-MM-dd_HH-mm-ss", TimeZone.getTimeZone("Asia/Shanghai"))
        attributes["Author"] = "angcyo"
        attributes["Copyright"] = "Wayto"
        attributes["versionName"] = dexConfig.versionName
        attributes["versionCode"] = dexConfig.versionCode
    }

    dexConfig.jarPath = archiveFile.get().getAsFile().getAbsolutePath()
}

task jarToDex(dependsOn: ['makeJar'], type: Exec) {
    //藉助windows的cmd命令行執行
    commandLine 'cmd'

    doFirst {
        //jar文件對象
        def srcFile = file(dexConfig.jarPath)
        //需要生成的dex文件對象
        def desFile = file(srcFile.parent + "/" + srcFile.name.substring(0, srcFile.name.lastIndexOf(".")) + ".dex")

        dexConfig.dexPath = desFile.getAbsolutePath()

        //此行可以不需要
        workingDir srcFile.parent

        //拼接dx.bat執行的參數
        def list = []
        list.add("/c")
        list.add("dx")
        list.add("--dex")
        list.add("--output")
        list.add(desFile)
        list.add(dexConfig.jarPath)

        //設置參數到cmd命令行
        args list
    }
}


羣內有各(pian)種(ni)各(jin)樣(qun)的大佬,等你來撩.

聯繫作者

點此快速加羣

請使用QQ掃碼加羣, 小夥伴們都在等着你哦!

關注我的公衆號, 每天都能一起玩耍哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章