Android 解决 API '.getPackageApplication()' is obsolete 的小问题

android studio 教程 | 2019-01-22 20:39

今天升级了 Android Studio,自然而然也就升级 Gradle(3.2.1 --> 3.3.0),于是乎,出现了一个警告,对于一个强迫症者来说,这能忍?才怪

先看看这个红色警告是啥

嗯,好像是什么东西废弃了,问题的源头倒是很好找,因为下面这段代码

applicationVariants.all { variant ->    variant.outputs.each { output ->        def outputFile = output.outputFile        if (outputFile == null) return        def fileName        if (output.name == 'debug') {            fileName = "AppName-v${defaultConfig.versionName}-c${defaultConfig.versionCode}-test.apk"        } else {            fileName = "AppName-v${defaultConfig.versionName}-c${defaultConfig.versionCode}.apk"        }        output.outputFileName = fileName    }}这是写在 build.gradle 中用于自定义生成 apk 名的一段代码,短短几行,却经历过不少辛酸,之前好几次升级也是因为方法废弃被迫修改,看来现在又得改了

根据提示,是因为 variantOutput.getPackageApplication() 这个方法废弃了,要咱们替换成 variant.getPackageApplicationProvider() 问题是,上面短短的几行代码,哪有用到这个方法,既然明里没有,那肯定是暗地里用了,经过排查这个问题是由 output.outputFile 引起的,说明在调用 getOutputFile() 这个方法时,方法里调用了 getPackageApplication() 这个方法,既然是暗里调用,那只能去看源码了

这里小插一下

def outputFile = output.outputFileif (outputFile == null) return这段代码此时用处其实并不大,删除即可,但之所以一直留到了现在,是因为之前有用到它来修改 apk 的保存路径,虽然现在只用到了修改 apk 的文件名,路径并没有修改,但万一哪天用到呢,既然现在是这段代码造成的问题,那肯定以后也不能用这段代码来修改保存路径了,so,进入源码了解下

首先进入 gradle-3.3.0-sources.jar ,那怎么进入呢,如下点击

便进入源码文件

经过一番寻找,找到了 getApplicationVariants() 方法所在

代码是这样的

public DomainObjectSet<ApplicationVariant> getApplicationVariants() {        return applicationVariantList;}这样我们就知道了  applicationVariants.all { variant -> 中的 variant 是 ApplicationVariant ,但是 ApplicationVariant 是一个接口

package com.android.build.gradle.api;import com.android.build.gradle.internal.api.TestedVariant;/** * A Build variant and all its public data. */public interface ApplicationVariant extends ApkVariant, TestedVariant {}继续寻找具体类,然后找到了 ApplicationVariantImpl ,路径

接着,我们继续寻找 variant.outputs.each { output -> 中的 output 变量所指,在 BaseVariant 中找到

/** * Returns the variant outputs. There should always be at least one output. * * @return a non-null list of variants. */@NonNullDomainObjectCollection<BaseVariantOutput> getOutputs();output 是 BaseVariantOutput ,会发现 BaseVariantOutput 又是一个接口,面向接口编程的坏处就是,别人看的时候要一层层剥离,才能找到内心,最终发现具体类是 ApkVariantOutputImpl ,路径

现在来看 def outputFile = output.outputFile 这段代码暗地里做了啥

@NonNull@Overridepublic File getOutputFile() {    PackageAndroidArtifact packageAndroidArtifact = getPackageApplication();    if (packageAndroidArtifact != null) {        return new File(            packageAndroidArtifact.getOutputDirectory(), apkData.getOutputFileName());    } else {        return super.getOutputFile();    }}终于找到问题源头了,在 getOutputFile() 方法里头调用了 getPackageApplication() 方法,跟我们一开始分析的一样,但这是 gradle 的源码,我们没法修改,所有只能不要再使用 getOutputFile() 方法了

那如果要更改输出路径的时候该怎么办呢?不是建议让我们用 getPackageApplicationProvider() 方法么,我们来看看这个方法得到的是啥?

/** * Returns the packaging task * * <p>Prefer this to {@link #getPackageApplication()} as it triggers eager configuration of the * task. */@NullableTaskProvider<PackageAndroidArtifact> getPackageApplicationProvider();一个任务提供者,实际起作用的是 PackageAndroidArtifact ,所有直接看其源码,路径

里边东西不少,我们只取所需

protected File outputDirectory;就它了,现在我要把 apk 输出到项目根目录下,就可以这样写了

applicationVariants.all { variant ->    variant.outputs.each { output ->        variant.packageApplicationProvider.get().outputDirectory = new File(project.rootDir.absolutePath + "/apk")        def fileName        if (output.name == 'debug') {            fileName = "AppName-v${defaultConfig.versionName}-c${defaultConfig.versionCode}-test.apk"        } else {            fileName = "AppName-v${defaultConfig.versionName}-c${defaultConfig.versionCode}.apk"        }        output.outputFileName = fileName    }}Perfect!烦人的警告也没有了