AS--›Gradle 4.1.0/4.0/3.3/3.0 修改APK生成路径和文件名(附AAR修改方式以及分析过程)

2020-10-20

AS4.1正式版发布: 下载地址

2020-5-29
AS4.0正式发布了. 下载地址

2019-01-15
跟上时代的步伐, AS3.3正式发布了. 下载地址

注意: 文中的版本号, 统一是com.android.tools.build:gradle:xxx的版本号, 并非gradle本身的版本号.

Gradle4.1 修改apk输出目录和文件名

android {
    applicationVariants.all { variant ->
        if (variant.buildType.name != "debug") {
            variant.packageApplicationProvider.get().outputDirectory = rootProject.file("/apk")
        }
        variant.outputs.forEach {
            it.outputFileName = "test.apk"
        }
    }
}

分析过程:点击查看

Gradle4.0 修改apk输出目录和文件名

android {
    applicationVariants.all { variant ->
        if (variant.buildType.name != "debug") {
            variant.packageApplicationProvider.get().outputDirectory = rootProject.file("/apk")
        }
        variant.outputs.forEach {
            it.apkData.outputFileName = "test.apk"
        }
    }
}

流程分析方法:点击查看

Gradle3.3 修改apk输出目录和文件名

Gradle3.3 以上的方法

/*Gradle3.3 以上的方法*/
applicationVariants.all { variant ->
   if (variant.buildType.name != "debug") {
       variant.getPackageApplicationProvider().get().outputDirectory = new File(project.rootDir.absolutePath + "/apk")
   }

   variant.getPackageApplicationProvider().get().outputScope.apkDatas.forEach { apkData ->
       apkData.outputFileName = ((project.name != "app") ? project.name : rootProject.name) + "-" +
               variant.versionName + "_" +
               variant.flavorName + "_" +
               variant.buildType.name + "_" +
               ".apk"
   }
}

以下是旧文:

同样的, Gradle 插件也更新到了3.0, 但是当我们更新到3.0的时候, 很多dsl 无法使用了, 其中就有一个本人比较喜欢的api改变了, 就是修改打包生成的APK文件名.

在未更新之前:

  getApplicationVariants().all { variant ->
      variant.outputs.each { output ->
          def fileName = "AppName-${defaultConfig.versionName}_" +
                  "${releaseTime()}_" +
                  "${variant.productFlavors[0].name}_" +
                  "${variant.buildType.name}_" +
                  "${if (variant.signingConfig == null) "unsigned" else variant.signingConfig.name}.apk"
          //可以通过这个方法修改输出文件名        
          output.outputFile = new File(output.outputFile.parent, fileName)
      }
  }

更新3.0之后, 上面的方法就会报错.

Gradle3.0 以下方法修改

android {
	...
	applicationVariants.all { variant ->
	    //这个修改输出的APK路径
	 if (variant.buildType.name != "debug") {//防止AS无法安装debug包(apk)
	    variant.getPackageApplication().outputDirectory = new File(project.rootDir.absolutePath + "/apk")
	 }
	    variant.getPackageApplication().outputScope.apkDatas.forEach { apkData ->
	        //这个修改输出APK的文件名
	        apkData.outputFileName = "AppName-" +
	                variant.versionName + "_" +
	                apk_time + "_" +
	                variant.flavorName + "_" +
	                variant.buildType.name + "_" +
	                variant.signingConfig.name +
	                ".apk"
	    }
	}
	...
}

具体的API更改说明可以查看:
https://developer.android.google.cn/studio/build/gradle-plugin-3-0-0-migration.html#variant_api
据说是为了加快编译速度.


同时
更新到Gradle 3.0之后,如果你有 productFlavors, 那么必须定义 flavorDimensions, 其次生成的apk name会根据productFlavors.name命名.

android {
	...
	flavorDimensions "type" //这个是必须的
	...
	productFlavors{
		dev{
            dimension "type"  //并且必须使用这个dimension
		}
		pre{
            dimension "type" //并且必须使用这个dimension
		}
		apk{
            dimension "type" //并且必须使用这个dimension
		}
	}
}

apk名如下:
dev-debug.apk
dev-release.apk
pre-debug.apk
pre-release.apk
apk-debug.apk
apk-release.apk

如果你声明了多个 productFlavors, 那么每个都要使用, 否则会编译不过.

android {
	...
	flavorDimensions "type", "type2"
	...
	productFlavors{
		dev{
            dimension "type" 
		}
		pre{
            dimension "type" 
		}
		apk{
            dimension "type2" //type2 也必须使用
		}
	}
}

这样生成的apk名字就会是 (type 对应的 productFlavors.name )+ (type2 对应的 productFlavors.name) .
上面的就会是:
devApk-debug.apk
devApk-release.apk
preApk-debug.apk
preApk-release.apk
这样就可以通过productFlavors.name达到修改APK的文件名.


修改AAR输出路径,和AAR文件名.

分析步骤:

  1. 通过DSL语句, 拿到对应的Java处理类
  2. 通过Java处理类, 找到对应的成员变量进行修改.

a.LibraryPlugin

//DSL语句
apply plugin: 'com.android.library'

//拿到插件对应的Java类
println project.plugins.findPlugin("com.android.library").class

输出结果:

//找到Java类LibraryPlugin
class com.android.build.gradle.LibraryPlugin

b.LibraryExtension

android {
    compileSdkVersion 28
    defaultConfig {
       ...
    }

    buildTypes {
       ...
    }
    //DSL语句
    println it.class
}

输出结果:

//找到Java类LibraryExtension
class com.android.build.gradle.LibraryExtension_Decorated

c.LibraryVariantImpl

通过查看LibraryExtension类源码, 找到方法.

 public DefaultDomainObjectSet<LibraryVariant> getLibraryVariants() {
    return libraryVariantList;
 }

打印此方法所有值:

android {
    compileSdkVersion 28
    defaultConfig {
       ...
    }

    buildTypes {
       ...
    }
    
    //DSL语句
    libraryVariants.all { variant ->
        println variant.class
    }
}

输出结果:

//找到关键类LibraryVariantImpl
class com.android.build.gradle.internal.api.LibraryVariantImpl_Decorated

d.LibraryVariantOutputImpl

通过查看类LibraryVariantImpl的源码和继承关系, 找到方法:

@NonNull
@Override
public DomainObjectCollection<BaseVariantOutput> getOutputs() {
    return outputs;
}

枚举打印输出:

variant.outputs.all { output ->
    println  output.class
}
//找到关键类LibraryVariantOutputImpl
class com.android.build.gradle.internal.api.LibraryVariantOutputImpl_Decorated

e.apkData

通过查看LibraryVariantOutputImpl源码, 找到方法和成员变量:

//关键成员
@NonNull protected final ApkData apkData;

//方法
@Override
@NonNull
protected ApkData getApkData() {
    return apkData;
}

f.outputFileName

通过查看ApkData源码, 找到成员变量:

//找到关键点, 见名知意.肯定是用来修改文件名的.
private String outputFileName;

通过查看LibraryVariantOutputImpl源码, 还找到方法:

//关键方法getOutputFile, 
//在老版本的android build gradle中,
//可以直接通过outputFile成员变量, 修改路径和文件名.
//新版本不行了, 区别就在下面的方法中.
@NonNull
@Override
public File getOutputFile() {
    Zip packageTask = getPackageLibrary();
    if (packageTask != null) {
        return new File(packageTask.getDestinationDir(), apkData.getOutputFileName());
    } else {
        return super.getOutputFile();
    }
}

通过上面方法的getOutputFile, 能知道.
文件名由apkData.getOutputFileName()决定;
文件路径由packageTask.getDestinationDir()决定.

so

文件路径修改

output.packageLibrary.destinationDir = new File(project.rootDir.absolutePath + "/aar")

仔细观察会发现packageLibraryLibraryVariantImpl类中, 就有方法可以获取:

//AS 3.0版本
@Override
@Nullable
public Zip getPackageLibrary() {
    variantData
            .getScope()
            .getGlobalScope()
            .getDslScope()
            .getDeprecationReporter()
            .reportDeprecatedApi(
                    "variant.getPackageLibraryProvider()",
                    "variant.getPackageLibrary()",
                    TASK_ACCESS_DEPRECATION_URL,
                    DeprecationReporter.DeprecationTarget.TASK_ACCESS_VIA_VARIANT);
    return variantData.getTaskContainer().getBundleLibraryTask().getOrNull();
}

//AS 3.3版本
@Nullable
@Override
public TaskProvider<Zip> getPackageLibraryProvider() {
    //noinspection unchecked
    return (TaskProvider<Zip>) variantData.getTaskContainer().getBundleLibraryTask();
}

最终修改方式如下:

android {
	...
	libraryVariants.all { variant ->
	    if (variant.buildType.name != "debug") {
	        variant.getPackageLibraryProvider().get().destinationDir = new File(project.rootDir.absolutePath + "/apk")
	    }
	
	    variant.outputs.all { output ->
	        output.apkData.outputFileName = ((project.name != "app") ? project.name : rootProject.name) + "-" +
	                defaultConfig.versionName + "_" +
	                variant.buildType.name +
	                ".aar"
	    }
	}
	...
}

如果对分析过程感兴趣的童学可以 点击此处阅读


Gradle全版本迅雷下载地址

通用下载地址: https://services.gradle.org/distributions/gradle-xxx.zip


比如:需要下载gradle-6.7的版本:

不包含源码的zip包
https://services.gradle.org/distributions/gradle-6.7-bin.zip

只有源码的zip包
https://services.gradle.org/distributions/gradle-6.7-src.zip

即有jar包, 又有源码的zip包
https://services.gradle.org/distributions/gradle-6.7-all.zip

版本-all-bin+src的组合.

按需下载,复制url打开迅雷,新增任务.即可!

Gradle发行版本列表查看:https://gradle.org/releases/


群内有各(pian)种(ni)各(jin)样(qun)的大佬,等你来撩.

联系作者

点此快速加群

请使用QQ扫码加群, 小伙伴们在等着你哦!

关注我的公众号, 每天都能一起玩耍哦!

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页