阅读 91

Gradle读源码的笔记(1)

起因

2020-04-13上班遇到一个问题,应用引用的第三方so库、jar包和初始化java代码,在不同的车机上有差异,我很自然地想到,Gradle的productFlavor应该可以处理,但尝试了一天都没有成功,就放弃了。

有点不服气,决定看一看Gradle的源码。因为我对Gradle的了解特别少,就只能硬看,边看边猜,不一定保证对。但如果不记录下来,很快就会忘记的。

入口

查资料发现,build.gradle中的android应该在,

META-INF/gradle-plugins/com.android.application.property

implementation-class=com.android.build.gradle.AppPlugin

class AppPlugin extends AbstractAppPlugin {
    @Override
    @NonNull
    protected Class<? extends AppExtension> getExtensionClass() {
        return BaseAppModuleExtension.class;
    }
}

public abstract class AbstractAppPlugin extends BasePlugin<AppExtensionImpl> {
    @NonNull
    @Override
    protected BaseExtension createExtension(...) {
        return project.getExtensions()
                .create("android", getExtensionClass(), ...);
    }
}
复制代码

这里的createExtension()就是把gradle文件中的android读取并解析成getExtensionClass()对应的对象,在application类型的工程中就是BaseAppModuleExtension对象,而在lib类型的工程中,则是LibraryExtension

META-INF/gradle-plugins/android-library.property

implementation-class=com.android.build.gradle.LibraryPlugin

public class LibraryPlugin extends BasePlugin<LibraryExtensionImpl> {
    @NonNull
    @Override
    protected BaseExtension createExtension(...) {
        return project.getExtensions()
                .create("android", getExtensionClass(), ...);
    }
    
    @NonNull
    protected Class<? extends BaseExtension> getExtensionClass() {
        return LibraryExtension.class;
    }
}
复制代码

根据工程类型的不同,android实际对应的类也不同。

暂时不管library, 继续看application,BaseAppModuleExtension类:

open class BaseAppModuleExtension(...): AppExtension(...) {
    var dynamicFeatures: MutableSet<String> = mutableSetOf()
    val bundle: BundleOptions...
    fun bundle(action: Action<BundleOptions>)...
}
复制代码

看内容应该是新出的App Bundle,还没了解过相关内容,跳过,去看父类AppExtension

public class AppExtension extends TestedExtension {
    private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList
            = new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class);
    
    public DomainObjectSet<ApplicationVariant> getApplicationVariants() {
        return applicationVariantList;
    }

    @Override
    public void addVariant(BaseVariant variant) {
        applicationVariantList.add((ApplicationVariant) variant);
    }
}
复制代码

现在出现两个问题:

  1. ApplicationVariant是什么
  2. DomainObjectSet是什么

Q1 ApplicationVariant是什么

对于问题1,我查到了一段代码,用于定义打包APK时的文件名:

// 没有用lambda, 直接用接口, 方便理解
applicationVariants.all(new Action<ApplicationVariant>() {
    @Override
    void execute(ApplicationVariant applicationVariant) {
        applicationVariant.outputs.forEach(new Consumer<BaseVariantOutput>() {
            @Override
            void accept(BaseVariantOutput baseVariantOutput) {
                println "baseVariantOutput.baseName = ${baseVariantOutput.baseName}"
                println "baseVariantOutput.dirName = ${baseVariantOutput.dirName}"
                println "baseVariantOutput.name = ${baseVariantOutput.name}"
                baseVariantOutput.outputFileName = "Hello_${applicationVariant.buildType.name}_Time.apk"

                // println "baseVariantOutput.class.name = ${baseVariantOutput.class.name}"
                println "baseVariantOutput.outputFileName = ${baseVariantOutput.outputFileName}"
            }
        })
    }
})

baseVariantOutput.baseName = debug
baseVariantOutput.dirName = 
baseVariantOutput.name = debug
baseVariantOutput.baseName = release
baseVariantOutput.dirName = 
baseVariantOutput.name = release

baseVariantOutput.class.name = com.android.build.gradle.internal.api.ApkVariantOutputImpl_Decorated
baseVariantOutput.outputFileName = Hello_debug_Time.apk
baseVariantOutput.outputFileName = Hello_release_Time.apk

public interface BaseVariantOutput extends OutputFile {
    ProcessAndroidResources getProcessResources();
    TaskProvider<ProcessAndroidResources> getProcessResourcesProvider();
    ManifestProcessorTask getProcessManifest();
    TaskProvider<ManifestProcessorTask> getProcessManifestProvider();
    Task getAssemble();
    String getName();
    String getBaseName();
    String getDirName();
    
    // interface OutputFile extends VariantOutput
    File getOutputFile();
    
    // interface VariantOutput
    String getOutputType();
    Collection<String> getFilterTypes();
    Collection<FilterData> getFilters();
    OutputFile getMainOutputFile();
    Collection<? extends OutputFile> getOutputs();
}
复制代码

跟这段代码时有个地方很疑惑,baseName, name, dirName, 这三个变量可以直接点出来,但outputFileName不行,BaseVariantOutput一路查看父类,都没有发现这个变量,它是从哪里冒出来的。直到我打印了它的class:ApkVariantOutputImpl的父类BaseVariantOutputImpl中定义了outputFileName.setter/getter。

打印ApplicationVariant的实现类,是 : com.android.build.gradle.internal.api.ApplicationVariantImpl_Decorated,同样的参数ApplicationVariant只是个接口,buildType定义在实现类中。

这样的语法实在太反Java直觉了,跟代码的时候特别艰难。

同时也看到了,applicationVariant.buildType=release/debug,而它们就是

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    
    // debug {} 是默认的
    // internal {}
}
复制代码

如果在buildTypes中定义其他的类型比如internal,那段遍历代码也会把它打印出来。

暂时先不管自定义,默认的debug是怎么默认添加的?实现方式是碰巧看到的 :

ApplicationVariantFactory
@Override
public void createDefaultComponents(
        @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
        @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
        @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
    // must create signing config first so that build type 'debug' can be initialized
    // with the debug signing config.
    signingConfigs.create(DEBUG);
    buildTypes.create(DEBUG);
    buildTypes.create(RELEASE);
}

called by BasePlugin:
private void configureExtension() ->
private void basePluginApply(@NonNull Project project) ->
public final void apply(@NonNull Project project) | 入口
最初的AbstractAppPlugin.createExtension()就是在这个apply()方法中被调用,作为插件添加到工程中。
复制代码

可以看到,signingConfigs也指定了默认debug值,这就是为什么:debug模式下可以直接运行,release模式不可以运行,因为默认没有配置签名工具。

此时又出现一个新问题,NamedDomainObjectContainer是什么东西?Gradle的这些类继承关系有点乱,不过它和DomainObjectSet都是亲戚关系,就把它放在Q2一起查。

Q2 DomainObjectContainer的一大堆子类

public interface DomainObjectCollection<T> extends Collection<T> {
    // 根据类型创建一个实例
    <S extends T> DomainObjectCollection<S> withType(Class<S> type);
    <S extends T> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction);
    <S extends T> DomainObjectCollection<S> withType(Class<S> type, Closure configureClosure);

    DomainObjectCollection<T> matching(Spec<? super T> spec);
    DomainObjectCollection<T> matching(Closure spec);

    // 从名字看上去像添加/删除时的回调
    Action<? super T> whenObjectAdded(Action<? super T> action);
    void whenObjectAdded(Closure action);
    Action<? super T> whenObjectRemoved(Action<? super T> action);
    void whenObjectRemoved(Closure action);

    // 遍历执行Action/闭包
    void all(Action<? super T> action);
    void all(Closure action);

    // 找出所有符合条件的对象
    Collection<T> findAll(Closure spec);
}
复制代码

withType方法暂时没找到哪里有调用,从其他几个方法可以猜出,它其实就是对Collection做了封装,比如子类public interface DomainObjectSet<T> extends DomainObjectCollection<T>, Set<T>表示它想封装的Collection是Set,不希望有重复值。 NamedDomain 就是比 Demain 多了Namer, 简单猜一下就是根据这个namer对应build.gradle中的配置项名字。

public interface NamedDomainObjectContainer<T> extends NamedDomainObjectSet<T> {
    T create(String name);
    ...
}

public interface NamedDomainObjectCollection<T> extends DomainObjectCollection<T> {
    Namer<T> getNamer();
    ...
}

println "android.buildTypes.class.name = ${android.buildTypes.class.name}"
android.buildTypes.class.name = org.gradle.api.internal.FactoryNamedDomainObjectContainer_Decorated
复制代码

NamedDomainObjectContainer.create(name) 倒是在前面的 createDefaultComponents() 看到有调用,可惜在源码里找不到这个实现类,看不到具体是怎么实现的,不过可以猜得出来:

此处的泛型表示它想维护一个T类型对象的列表,create(name) 则会根据参数name去生成新T对象并添加到列表中。

比如 buildTypes.create("debug") 会创建一个 名字是debug的BuildType,在build.gradle.buildTypes中自定义一个internal时,猜测最终也是通过 buildTypes.create("internal") 创建一个 名字是internal的BuildType

下一篇

接下来看 AppExtension extends TestedExtension父类