一、插件相关API
PluginAware
主要定义了插件相关API。
public interface PluginAware {
PluginContainer getPlugins();
void apply(Closure closure);
void apply(Action< ? super ObjectConfigurationAction> action);
void apply(Map<String, ?> options);
PluginManager getPluginManager();
}
应用插件
apply plugin: 'java'
,表示应用Java插件。这个语句调用了apply()
方法,后面的plugin: 'java'
是一个Map类型参数。
apply plugin: MyClass
表示应用指定class实现的插件,将在后面的Plugin
中介绍。
org.gradle.api.Plugin
Plugin
用于定义插件。Gradle提供了完整的API框架,而很多工作实际是由插件实现的。Gradle内置了Java、Groovy等几种基础插件,也可以自定义插件。
Plugin
接口很简单,只有一个apply方法。
public interface Plugin<T> {
/**
* Apply this plugin to the given target object.
*
* @param target The target object
*/
void apply(T target);
}
二、简易插件开发
下面的示例代码实现了HelloPlugin的简易插件,代码可直接写在build.gradle
中。
插件在apply(Project)
方法里,给Project创建了一个名为hello
的Extension和一个名为welcome
的Task;Task执行时读取Extension并打印字符串。
在build.gradle
执行到apply plugin: HelloPlugin
时,HelloPlugin.apply(Project)
方法被执行,从而Project有了hello
的Extension,于是后面可以调用hello {}
对插件进行配置。
class HelloExtension {
Boolean enable = true
String text = ''
}
class HelloPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.extensions.create('hello', HelloExtension)
project.task('welcome') {
doLast {
HelloExtension ext = project.extensions.hello;
println ext.enable ? "Hello ${ext.text}!" : 'HelloPlugin is disabled.'
}
}
}
}
apply plugin: HelloPlugin
hello {
enable = true
text = 'Gradle'
}
在命令行中执行结果如下:
$ ./gradlew welcome
:welcome
Hello Gradle!
BUILD SUCCESSFUL
Android transform
android gradle plugin 提供了 transform api 用来在 .class to dex 过程中对 class 进行处理,可以理解为一种特殊的 Task,因为 transform 最终也会转化为 Task 去执行
要实现 transform 需要继承 com.android.build.api.transform.Transform 并实现其方法,实现了 Transform 以后,要想应用,就调用project.android.registerTransform()
public class MyTransform extends Transform {
@Override
public String getName() {
// 返回 transform 的名称,最终的名称会是 transformClassesWithMyTransformForDebug 这种形式
return "MyTransform";
}
@Override
public Set<QualifiedContent.ContentType> getInputTypes() {
/**
返回需要处理的数据类型 有 下面几种类型可选
public static final Set<ContentType> CONTENT_CLASS = ImmutableSet.of(CLASSES);
public static final Set<ContentType> CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
public static final Set<ContentType> CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
public static final Set<ContentType> CONTENT_NATIVE_LIBS = ImmutableSet.of(NATIVE_LIBS);
public static final Set<ContentType> CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);
public static final Set<ContentType> DATA_BINDING_ARTIFACT = ImmutableSet.of(ExtendedContentType.DATA_BINDING);
*/
return TransformManager.CONTENT_CLASS;
}
@Override
public Set<? super QualifiedContent.Scope> getScopes() {
/**
返回需要处理内容的范围,有下面几种类型
PROJECT(1), 只处理项目的内容
SUB_PROJECTS(4), 只处理子项目
EXTERNAL_LIBRARIES(16), 只处理外部库
TESTED_CODE(32), 只处理当前 variant 对应的测试代码
PROVIDED_ONLY(64), 处理依赖
@Deprecated
PROJECT_LOCAL_DEPS(2),
@Deprecated
SUB_PROJECTS_LOCAL_DEPS(8);
*/
return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT);
}
@Override
public boolean isIncremental() {
// 是否增量,如果返回 true,TransformInput 会包括一份修改的文件列表,返回 false,会进行全量编译,删除上一次的输出内容
return false;
}
@Override
void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
// 在这里处理 class
super.transform(transformInvocation)
// 在 transform 里,如果没有任何修改,也要把 input 的内容输出到 output,否则会报错
for (TransformInput input : transformInvocation.inputs) {
input.directoryInputs.each { dir ->
// 获取对应的输出目录
File output = transformInvocation.outputProvider.getContentLocation(dir.name, dir.contentTypes, dir.scopes, Format.DIRECTORY)
dir.changedFiles // 增量模式下修改的文件
dir.file // 获取输入的目录
FileUtils.copyDirectory(dir.file, output) // input 内容输出到 output
}
input.jarInputs.each { jar ->
// 获取对应的输出 jar
File output = transformInvocation.outputProvider.getContentLocation(jar.name, jar.contentTypes, jar.scopes, Format.JAR)
jar.file // 获取输入的 jar 文件
FileUtils.copyFile(jar.file, output) // input 内容输出到 output
}
}
}
}
// 注册 transform
android.registerTransform(new MyTransform())
三、插件开发
在 android studio 中创建一个 java module
在 src/main 目录下创建 groovy 目录,然后创建自己的包名和插件类
-
在 src/main 目录下创建 resources/META-INFO/gradle-plugins 目录,创建 myplugin.properties 文件,文件里内容是
implementation-class=com.*.MyPlugin // 这里是自己的插件类
-
修改 build.gradle 文件
// 引入 groovy 和 java 插件 apply plugin: 'groovy' apply plugin: 'java' buildscript { repositories { mavenLocal() maven { url 'https://maven.google.com' } jcenter() } } repositories { mavenLocal() maven { url 'https://maven.google.com' } } dependencies { compile gradleApi() compile localGroovy() compile 'com.android.tools.build:gradle:3.0.1' }
-
创建Plugin类
创建插件类,就可以写插件的代码了。插件类继承 Plugin,并实现 apply 接口,apply 就是在 build.gradle 里 apply plugin 'xxx' 的时候要调用的接口了
插件开发可以使用 groovy 和 java,使用 groovy 的话可以有更多的语法糖,开发起来更方便一些package com.binzi.plugin import org.gradle.api.Plugin import org.gradle.api.Project class MyPlugin implements Plugin<Project> { @Override void apply(Project project) { println("apply my plugin") } }
-
创建插件的task
我们再定义一个 task 类 MyTask,继承自 DefaultTask,简单的输出一些信息
package com.binzi.plugin import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction class MyTask extends DefaultTask { @TaskAction void action() { println('my task run') } }
然后在 plugin 中注册这个 task
class MyPlugin implements Plugin<Project> { @Override void apply(Project project) { println("apply my plugin") project.tasks.create("mytask", MyTask.class) } }
-
本地安装插件
这样一个简单的插件就开发好了,如何使用呢
我们首先需要在 build.gradle 中引入 maven 插件,并且配置 install 相关的属性apply plugin: 'maven' install { repositories.mavenInstaller { pom.version = '0.0.1' // 配置插件版本号 pom.artifactId = 'myplugin' // 配置插件标识 pom.groupId = 'com.binzi.plugin' // 配置插件组织 } }
之后执行 ./gradlew install 便会把插件安装在本地 maven 仓库
之后在使用的地方引入我们插件的 classpathclasspath 'com.binzi.plugin:myplugin:0.0.1'
加载插件
apply plugin; 'myplugin' // 这里的 myplugin 是前面说的 myplugin.properties 的名字
-
打包发布
在插件 build.gradle 里新增上传的配置如下
uploadArchives { repositories { mavenDeployer { repository(url: "mavenUrl") pom.version = '0.0.1' pom.artifactId = 'myplugin' } } }
Android Plugin主要流程
插件入口
在前面讲解自定义插件的时候说到过,要定义一个 xxx.properties 文件,里面声明插件的入口类,要知道 android gradle plugin 的入口类,看源码中android.properties 文件就可以,内容如下:
implementation-class=com.android.build.gradle.AppPlugin
这里定义了入口是 AppPlugin,AppPlugin 继承自AbstractAppPlugin,AbstractAppPlugin继承自BasePlugin。
AbstractAppPlugin 里没有做太多的操作,主要是重写了 createTaskManager 和 createExtension,剩下的大部分工作还是在 BasePlugin 里做的。
BasePlugin中的的入口函数是apply(0
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
这里主要调用
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
这里主要调用
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
这里主要调用basePluginApply和pluginSpecificApply,其中pluginSpecificApply是个抽象函数,在pluginSpecificApply中是个空实现,主要操作都在basePluginApply中。
下面来看看basePluginApply中都干了些啥:
//检查工程路径
checkPathForErrors();
//检查两个module是否有相同的id
checkModulesForErrors();
//PluginInitializer初始化
PluginInitializer.initialize(project);
//ProfilerInitializer初始化
ProfilerInitializer.init(project, projectOptions);
threadRecorder = ThreadRecorder.get();
// 使用project的选项配置worker
Workers.INSTANCE.initFromProject(
projectOptions,
ForkJoinPool.commonPool());
//profiler中写入plugin信息
ProcessProfileWriter.getProject(project.getPath())
.setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
.setAndroidPlugin(getAnalyticsPluginType())
.setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
.q(AnalyticsUtil.toProto(projectOptions));
if (!projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API)) {
//配置project
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);
//创建task
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null,
this::createTasks);
}
配置Project
private void configureProject() {
final Gradle gradle = project.getGradle();
ObjectFactory objectFactory = project.getObjects();
//配置项对应的model
extraModelInfo = new ExtraModelInfo(project.getPath(), projectOptions, project.getLogger());
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 = new DataBindingBuilder();
dataBindingBuilder.setPrintMachineReadableOutput(
SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);
if (projectOptions.hasRemovedOptions()) {
androidBuilder
.getIssueReporter()
.reportWarning(Type.GENERIC, projectOptions.getRemovedOptionsErrorMessage());
}
if (projectOptions.hasDeprecatedOptions()) {
extraModelInfo
.getDeprecationReporter()
.reportDeprecatedOptions(projectOptions.getDeprecatedOptions());
}
if (!projectOptions.getExperimentalOptions().isEmpty()) {
projectOptions
.getExperimentalOptions()
.forEach(extraModelInfo.getDeprecationReporter()::reportExperimentalOption);
}
// Enforce minimum versions of certain plugins
GradlePluginUtils.enforceMinimumVersionsOfPlugins(
project, androidBuilder.getIssueReporter());
//应用java插件
project.getPlugins().apply(JavaBasePlugin.class);
DslScopeImpl dslScope =
new DslScopeImpl(
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getDeprecationReporter(),
objectFactory);
//创建build缓存文件
@Nullable
FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);
globalScope =
new GlobalScope(
project,
new ProjectWrapper(project),
projectOptions,
dslScope,
androidBuilder,
sdkHandler,
registry,
buildCache);
project.getTasks()
.getByName("assemble")
.setDescription(
"Assembles all variants of all applications and secondary packages.");
// 添加build监听
gradle.addBuildListener(
new BuildListener() {
@Override
public void buildStarted(@NonNull Gradle gradle) {}
@Override
public void settingsEvaluated(@NonNull Settings settings) {}
@Override
public void projectsLoaded(@NonNull Gradle gradle) {}
@Override
public void projectsEvaluated(@NonNull Gradle gradle) {}
@Override
public void buildFinished(@NonNull BuildResult buildResult) {
// Do not run buildFinished for included project in composite build.
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();
});
DeprecationReporterImpl.Companion.clean();
}
});
createLintClasspathConfiguration(project);
}
配置Extension
private void configureExtension() {
...
extension = createExtension(
project,
projectOptions,
globalScope,
sdkHandler,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo);
taskManager = createTaskManager(
globalScope,
project,
projectOptions,
dataBindingBuilder,
extension,
sdkHandler,
variantFactory,
registry,
threadRecorder);
...
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
}
其中主要是调用 createExtension来加载gradle脚本中的配置信息,在使用解析完后的配置信息创建taskManager、variantManager等其他组件。
创建 task
private void createTasks() {
threadRecorder.record(
ExecutionType.TASK_MANAGER_CREATE_TASKS,
project.getPath(),
null,
() -> taskManager.createTasksBeforeEvaluate());
project.afterEvaluate(
CrashReporting.afterEvaluate(
p -> {
sourceSetManager.runBuildableArtifactsActions();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
project.getPath(),
null,
this::createAndroidTasks);
}));
}
createAndroidTasks 的调用时机是在 project.afterEvaluate 里调用的,这个时候所有模块配置已经完成了
在 BasePlugin.createAndroidTasks 里,是调用 VariantManager.createAndroidTasks 完成工作的。
四、Android Plugin主要 Task
1、Android 打包流程
官方介绍的流程如下:
- 编译器将您的源代码转换成 DEX(Dalvik Executable) 文件(其中包括 Android 设备上运行的字节码),将所有其他内容转换成已编译资源。
- APK 打包器将 DEX 文件和已编译资源合并成单个 APK。 不过,必须先签署 APK,才能将应用安装并部署到 Android 设备上。
- APK 打包器使用调试或发布密钥库签署您的 APK:
- 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时占用的内存。
首先我们看一下 打包一个 apk 需要哪些 task。
在项目根目录下执行命令:
./gradlew assembleDebug --console=plain
看一下输出结果
> Task :app:preBuild UP-TO-DATE
> Task :app:prepareLintJar UP-TO-DATE
> Task :app:preDebugBuild
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:checkDebugManifest
> Task :app:generateDebugBuildConfig
> Task :app:generateDebugSources
> Task :app:javaPreCompileDebug
> Task :app:mainApkListPersistenceDebug
> Task :app:generateDebugResValues
> Task :app:generateDebugResources
> Task :app:mergeDebugResources
> Task :app:createDebugCompatibleScreenManifests
> Task :app:processDebugManifest
> Task :app:processDebugResources
> Task :app:compileDebugJavaWithJavac
> Task :app:compileDebugSources
> Task :app:mergeDebugShaders
> Task :app:compileDebugShaders
> Task :app:generateDebugAssets
> Task :app:mergeDebugAssets
> Task :app:checkDebugDuplicateClasses
> Task :app:transformClassesWithDexBuilderForDebug
> Task :app:validateSigningDebug
> Task :app:signingConfigWriterDebug
> Task :app:mergeExtDexDebug
> Task :app:mergeDexDebug
> Task :app:mergeDebugJniLibFolders
> Task :app:transformNativeLibsWithMergeJniLibsForDebug
> Task :app:transformNativeLibsWithStripDebugSymbolForDebug
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:transformResourcesWithMergeJavaResForDebug
> Task :app:packageDebug
> Task :app:assembleDebug
Task 对应实现类
我们先看看每个 task 都是做什么的,以及其对应的实现类。
先回忆一下,我们在前面 android-gradle-plugin 主要流程分析里说到过,task 的实现可以在 TaskManager 里找到,创建 Task 的方法主要是两个,TaskManager.createTasksBeforeEvaluate() 和 ApplicationTaskManager.createTasksForVariantScope(),所以这些 task 的实现,也在这两个类里找就可以,下面列出了各个 task 的作用及实现类。
Task | 对应实现类 | 作用 |
---|---|---|
preBuild | 空 task,只做锚点使用 | |
preDebugBuild | 空 task,只做锚点使用,与 preBuild 区别是这个 task 是 variant 的锚点 | |
compileDebugAidl | AidlCompile | 处理 aidl |
compileDebugRenderscript | RenderscriptCompile | 处理 renderscript |
checkDebugManifest | CheckManifest | 检测 manifest 是否存在 |
generateDebugBuildConfig | GenerateBuildConfig | 生成 BuildConfig.java |
prepareLintJar | PrepareLintJar | 拷贝 lint jar 包到指定位置 |
generateDebugResValues | GenerateResValues | 生成 resvalues,generated.xml |
generateDebugResources | 空 task,锚点 | |
mergeDebugResources | MergeResources | 合并资源文件 |
createDebugCompatibleScreenManifests | CompatibleScreensManifest | manifest 文件中生成 compatible-screens,指定屏幕适配 |
processDebugManifest | MergeManifests | 合并 manifest 文件 |
splitsDiscoveryTaskDebug | SplitsDiscovery | 生成 split-list.json,用于 apk 分包 |
processDebugResources | ProcessAndroidResources | aapt 打包资源 |
generateDebugSources | 空 task,锚点 | |
javaPreCompileDebug | JavaPreCompileTask | 生成 annotationProcessors.json 文件 |
compileDebugJavaWithJavac | AndroidJavaCompile | 编译 java 文件 |
compileDebugNdk | NdkCompile | 编译 ndk |
compileDebugSources | 空 task,锚点使用 | |
mergeDebugShaders | MergeSourceSetFolders | 合并 shader 文件 |
compileDebugShaders | ShaderCompile | 编译 shaders |
generateDebugAssets | 空 task,锚点 | |
mergeDebugAssets | MergeSourceSetFolders | 合并 assets 文件 |
transformClassesWithDexBuilderForDebug | DexArchiveBuilderTransform | class 打包 dex |
transformDexArchiveWithExternalLibsDexMergerForDebug | ExternalLibsMergerTransform | 打包三方库的 dex,在 de增量的时候就不需要再 merge 了,节省时间 |
transformDexArchiveWithDexMergerForDebug | DexMergerTransform | 打包最终的 dex |
mergeDebugJniLibFolders | MergeSouceSetFolders | 合并 jni lib 文件 |
transformNativeLibsWithMergeJniLibsForDebug | MergeJavaResourcesTransform | 合并 jnilibs |
transformNativeLibsWithStripDebugSymbolForDebug | StripDebugSymbolTransform | 去掉 native lib 里的 debug 符号 |
processDebugJavaRes | ProcessJavaResConfigAction | 处理 java res |
transformResourcesWithMergeJavaResForDebug | MergeJavaResourcesTransform | 合并 java res |
validateSigningDebug | ValidateSigningTask | 验证签名 |
packageDebug | PackageApplication | 打包 apk |
assembleDebug | 空 task,锚点 |