破解Gradle 从Gradle Plugin 构建看APK打包流程解析
Posted 丶笑看退场
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了破解Gradle 从Gradle Plugin 构建看APK打包流程解析相关的知识,希望对你有一定的参考价值。
目前,android在进行构建APK,最常用到的就是Gradle打包。而要了解Android Apk打包的过程,就要深入了解Gradle Plugin的整个构建过程,在了解了之后,我们才能对Gradle Plugin开发游刃有余。
我们先了解一下APK内部的结构:
一、Android APK包结构
来看看一个正常的APK的结构。
我们可以打开build–outputs–apk–debug下的apk文件,就得到了下图,通常一个APK打包完之后,会有下面几个目录,用来存放不同的文件。
class.dex
java代码通过javac转化成class文件,在通过dx文件转化成dex文件 。
res
保存了处理后的二进制资源文件。
esoources.arsc
保存了资源id名称以及资源对应的值/路径的映射。
META-INF
用来验证APK签名,其中三个重要的文件MANIFEST.MT
,CERT.SF
,CERT.RSA
。
- MANIFEST.MF保存了所有文件对应的摘要
- CERT.SF 保存了MANIFEST.MF 中每条信息的摘要
- CERT.RSA 包含了对 CERT.SF 文件的签名以及签名用到的证书
AndroidManifest.xml
全局配置文件,这里是编译处理过的二进制的文件。
二、AppPlugin 构建流程
在分析前我们先编译下项目,直接左上方的编译按钮。可以看到会有以下输出:
:android-gradle-plugin-source:preBuild UP-TO-DATE
:android-gradle-plugin-source:preDebugBuild
:android-gradle-plugin-source:compileDebugAidl
:android-gradle-plugin-source:compileDebugRenderscript
:android-gradle-plugin-source:checkDebugManifest
:android-gradle-plugin-source:generateDebugBuildConfig
:android-gradle-plugin-source:prepareLintJar UP-TO-DATE
:android-gradle-plugin-source:generateDebugResValues
:android-gradle-plugin-source:generateDebugResources
:android-gradle-plugin-source:mergeDebugResources
:android-gradle-plugin-source:createDebugCompatibleScreenManifests
:android-gradle-plugin-source:processDebugManifest
:android-gradle-plugin-source:splitsDiscoveryTaskDebug
:android-gradle-plugin-source:processDebugResources
:android-gradle-plugin-source:generateDebugSources
.......
从这里知道使用gradle构建,其实在使用一个个Task执行。在分析这些Task源码之前,我们跟下Gradle Plugin的构建过程。
为了能够查看Android Gradle Plugin的源码,我们需要在项目中添加android grade plugin依赖,如下所示:
compileOnly 'com.android.tools.build:gradle:3.4.0'
好了可以开始看gradle源码了,但该从什么地方开始看起?
我们可以知道哦将一个module构建为一个Android项目,是在根目录下的build.gradle中配置了引入如下插件:
apply plugin: 'com.android.application'
我们再上文中讲过自定义插件,要定义一个xxx.properties
文件,里面声明插件的入口类。这里我们就知道了android gradle plugin
的入口类,只要找到com.android.application.properties
文件就可以了,然后可以看到内部标明了插件的实现类:
implementation-class=com.android.build.gradle.internal.plugins.AppPlugin
这里定义了入口是AppPlugin
,而AppPlugin是继承了BasePlugin
:
public class AppPlugin extends AbstractAppPlugin
.....
//应用指定plugin
@Override
protected void pluginSpecificApply(@NonNull Project project)
.....
//获取一个扩展类,会提供一个相对应的android extension
@Override
@NonNull
protected Class<? extends AppExtension> getExtensionClass()
return BaseAppModuleExtension.class;
AppPlugin里面并没有做太多的操作,大部分工作还是在BasePlugin中做的。而主要的apply
方法就是在BasePlugin
中,在这个方法里面会进行一些准备工作,如下所示:
public final void apply(@NonNull Project project)
CrashReporting.runAction(
() ->
basePluginApply(project);
pluginSpecificApply(project);
);
在apply方法里又调用了basePluginApply
方法,如下所示:
## BasePlugin.java
private void basePluginApply(@NonNull Project project)
// We run by default in headless mode, so the JVM doesn't steal focus.
System.setProperty("java.awt.headless", "true");
this.project = project;
this.projectOptions = new ProjectOptions(project);
//检查gradle的版本号
checkGradleVersion(project, getLogger(), projectOptions);
DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);
project.getPluginManager().apply(AndroidBasePlugin.class);
//路径检查
checkPathForErrors();
//检查module名字是否合规
checkModulesForErrors();
PluginInitializer.initialize(project);
ProfilerInitializer.init(project, projectOptions);
//记录方法耗时
threadRecorder = ThreadRecorder.get();
......
if (!projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API))
// 配置工程
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
project.getPath(),
null,
this::configureProject);
// 配置 Extension
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
project.getPath(),
null,
this::configureExtension);
// 创建 Tasks
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null,
this::createTasks);
else
......
可以看到在apply
方法里,threadRecoirder.recode()
是记录最后一个参数的路径和执行的时间点,在前面做了一些插件检查操作以及对插件进行初始化与配置相关的信息,其实主要是做了下面这些事:
// 配置项目,设置构建回调
this::configureProject
// 配置Extension
this::configureExtension
// 创建任务
this::createTasks
2.1 configurePoroject配置项目
这一阶段主要做了:
## BasePlugin
private void configureProject()
final Gradle gradle = project.getGradle();
.....
//AndroidSDK处理类
sdkHandler = new SdkHandler(project, getLogger());
if (!gradle.getStartParameter().isOffline()
&& projectOptions.get(BooleanOption.ENABLE_SDK_DOWNLOAD))
//相关配置依赖的下载处理
SdkLibData sdkLibData = SdkLibData.download(getDownloader(), getSettingsController());
sdkHandler.setSdkLibData(sdkLibData);
//创建AndroidBuilder
AndroidBuilder androidBuilder =
new AndroidBuilder(
project == project.getRootProject() ? project.getName() : project.getPath(),
creator,
new GradleProcessExecutor(project),
new GradleJavaProcessExecutor(project),
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getMessageReceiver(),
getLogger());
//创建 DataBindingBuilder 实例。
dataBindingBuilder = new DataBindingBuilder();
dataBindingBuilder.setPrintMachineReadableOutput(
SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);
if (projectOptions.hasRemovedOptions())
androidBuilder
.getIssueReporter()
.reportWarning(Type.GENERIC, projectOptions.getRemovedOptionsErrorMessage());
......
// 强制使用不低于当前所支持的最小插件版本,否则会抛出异常。
GradlePluginUtils.enforceMinimumVersionsOfPlugins(
project, androidBuilder.getIssueReporter());
// 应用 Java Plugin
project.getPlugins().apply(JavaBasePlugin.class);
DslScopeImpl dslScope =
new DslScopeImpl(
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getDeprecationReporter(),
objectFactory);
@Nullable
FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);
......
//给assemble任务添加甲描述
project.getTasks()
.getByName("assemble")
.setDescription(
"Assembles all variants of all applications and secondary packages.");
// project 执行完成结束后清除缓存操作
gradle.addBuildListener(
new BuildListener()
.......
@Override
public void buildFinished(@NonNull BuildResult buildResult)
if (buildResult.getGradle().getParent() != null)
return;
ModelBuilder.clearCaches();
sdkHandler.unload();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
project.getPath(),
null,
() ->
WorkerActionServiceRegistry.INSTANCE
.shutdownAllRegisteredServices(
ForkJoinPool.commonPool());
Main.clearInternTables();
);
// 当任务执行完成时,清楚dex缓存
DeprecationReporterImpl.Companion.clean();
);
.....
在这个方法里创建了sdkHandle
在代码编译的时候会用到android.jar
这个包。另外也创建了AndroidBuilder
对象,这个对象是用来合并manifest
和创建等作用。最后添加了gradle
的生命周期监听,在project
执行结束后清除了dex缓存。
2.2 configureExtension配置Extension
这个阶段就是配置extension的阶段,就是创建我们android块中的可配置对象。
- 创建了
BuildType
、ProductFlavor
、SignIngConfig
三个类型的Container,接着就传入到了createExtension
方法中。这里的AppExtension
也就是build.gradle
里的androidDSL闭包
,来看下createExtension
方法:
## AbstractAppPlugin.java
protected BaseExtension createExtension(
@NonNull Project project,
@NonNull ProjectOptions projectOptions,
@NonNull GlobalScope globalScope,
@NonNull SdkHandler sdkHandler,
@NonNull NamedDomainObjectContainer<BuildType> buildTypeContainer,
@NonNull NamedDomainObjectContainer<ProductFlavor> productFlavorContainer,
@NonNull NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
@NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,
@NonNull SourceSetManager sourceSetManager,
@NonNull ExtraModelInfo extraModelInfo)
return project.getExtensions()
.create(
"android",
getExtensionClass(),
project,
projectOptions,
globalScope,
sdkHandler,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo,
isBaseApplication);
我们也可以看出android配置块是从Gradle构建中出来的。
- 依次创建了一些管理类
variantFactory
、taskManager
和variantManager
。其中TaskManager是创建具体任务的管理类,VariantFactory
是构建变体的工厂类,主要生成构建变体的对象。 - 配置了
signingConfigContainer
、buildTypeContainer
和productFlavorContainer
的回调,每个配置的回调都会放入variantManager
中进行管理。
## BasePlugin.java
//将 whenObjectAdded callbacks 映射到 singingConfig 容器之中。
signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);
buildTypeContainer.whenObjectAdded(
buildType ->
if (!this.getClass().isAssignableFrom(DynamicFeaturePlugin.class))
SigningConfig signingConfig =
signingConfigContainer.findByName(BuilderConstants.DEBUG);
buildType.init(signingConfig);
else
// initialize it without the signingConfig for dynamic-features.
buildType.init();
variantManager.addBuildType(buildType);
);
//将 whenObjectAdded callbacks 映射到 productFlavor 容器之中。
productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);
- 创建默认的debug签名,创建debug和release两个buildType。
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
真正的实现是在ApplicationVariantFactory
中:
public void createDefaultComponents(
@NonNull NamedDomainObjectContainer<BuildType> buildTypes,
@NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
@NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs)
// 必须首先创建签名配置,以便可以使用调试签名配置初始化构建类型“debug”。
signingConfigs.create(DEBUG);
buildTypes.create(DEBUG);
buildTypes.create(RELEASE);
2.3 createTasks创建 task
在BasePlugin
的createTask
方法里开始构建需要的Task,主要有两步,创建不依赖flavor
的task
和创建依赖配置项的配置的task
。
## BasePlugin.java
private void createTasks()
//在evaluate之前创建Tasks
threadRecorder.record(
ExecutionType.TASK_MANAGER_CREATE_TASKS,
project.getPath(),
null,
() -> taskManager.createTasksBeforeEvaluate());
//创建Android Tasks
project.afterEvaluate(
CrashReporting.afterEvaluate(
p ->
sourceSetManager.runBuildableArtifactsActions();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
project.getPath(),
null,
this::createAndroidTasks);
));
- createTasksBeforeEvaluate()
在这个方法里给容器注册了一系列的Task,包括uninstallAll,deviceCheck,connectedCheck,preBuild,extractProguardFiles,sourceSets,assembleAndroidTest,compileLint,lint,lintChecks,cleanBuildCacheresolveConfigAttr,consumeConfigAttr。
- createAndroidTasks()
在这个方法主要是生成flavors相关数据,并根据flavor创建与之对应的Task实例并注册到Task当中。
## BaePlugin.java
@VisibleForTesting
final void createAndroidTasks()
......
// Project Path、CompileSdk、BuildToolsVersion、Splits、KotlinPluginVersion、FirebasePerformancePluginVersion 写入project配置
ProcessProfileWriter.getProject(project.getPath())
.setCompileSdk(extension.getCompileSdkVersion())
.setBuildToolsVersion(extension.getBuildToolsRevision().toString())
.setSplits(AnalyticsUtil.toProto(extension.getSplits()));
String kotlinPluginVersion = getKotlinPluginVersion();
if (kotlinPluginVersion != null)
ProcessProfileWriter.getProject(project.getPath())
.setKotlinPluginVersion(kotlinPluginVersion);
if (projectOptions.get(BooleanOption.INJECT_SDK_MAVEN_REPOS))
sdkHandler.addLocalRepositories(project);
// 创建应用的task
List<VariantScope> variantScopes = variantManager.createAndroidTasks();
......
我们主要来看下variantManager
的createAndroidTasks
方法:
## VariantManager.java
public List<VariantScope> createAndroidTasks()
variantFactory.validateModel(this);
variantFactory.preVariantWork(project);
if (variantScopes.isEmpty())
//构建flavor变体
populateVariantDataList();
taskManager.createTopLevelTestTasks(!productFlavors.isEmpty());
for (final VariantScope variantScope : variantScopes)
//为变体数据创建相对应的task
createTasksForVariantData(variantScope);
taskManager.createSourceSetArtifactReportTask(globalScope);
taskManager.createReportTasks(variantScopes);
return variantScopes破解Gradle 从Gradle Plugin 构建看APK打包流程解析
破解Gradle Gradle Plugin技术及玩转transform
破解Gradle Gradle Plugin技术及玩转transform
破解Gradle Gradle Plugin技术及玩转transform