一坪仇、插件相關(guān)API
PluginAware
主要定義了插件相關(guān)API。
public interface PluginAware {
PluginContainer getPlugins();
void apply(Closure closure);
void apply(Action< ? super ObjectConfigurationAction> action);
void apply(Map<String, ?> options);
PluginManager getPluginManager();
}
應(yīng)用插件
apply plugin: 'java'
,表示應(yīng)用Java插件鲁驶。這個語句調(diào)用了apply()
方法,后面的plugin: 'java'
是一個Map類型參數(shù)舞骆。
apply plugin: MyClass
表示應(yīng)用指定class實現(xiàn)的插件钥弯,將在后面的Plugin
中介紹。
org.gradle.api.Plugin
Plugin
用于定義插件督禽。Gradle提供了完整的API框架脆霎,而很多工作實際是由插件實現(xiàn)的。Gradle內(nèi)置了Java狈惫、Groovy等幾種基礎(chǔ)插件睛蛛,也可以自定義插件。
Plugin
接口很簡單胧谈,只有一個apply方法忆肾。
public interface Plugin<T> {
/**
* Apply this plugin to the given target object.
*
* @param target The target object
*/
void apply(T target);
}
二、簡易插件開發(fā)
下面的示例代碼實現(xiàn)了HelloPlugin的簡易插件菱肖,代碼可直接寫在build.gradle
中客冈。
插件在apply(Project)
方法里,給Project創(chuàng)建了一個名為hello
的Extension和一個名為welcome
的Task蔑滓;Task執(zhí)行時讀取Extension并打印字符串郊酒。
在build.gradle
執(zhí)行到apply plugin: HelloPlugin
時遇绞,HelloPlugin.apply(Project)
方法被執(zhí)行,從而Project有了hello
的Extension燎窘,于是后面可以調(diào)用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'
}
在命令行中執(zhí)行結(jié)果如下:
$ ./gradlew welcome
:welcome
Hello Gradle!
BUILD SUCCESSFUL
Android transform
android gradle plugin 提供了 transform api 用來在 .class to dex 過程中對 class 進行處理,可以理解為一種特殊的 Task褐健,因為 transform 最終也會轉(zhuǎn)化為 Task 去執(zhí)行
要實現(xiàn) transform 需要繼承 com.android.build.api.transform.Transform 并實現(xiàn)其方法付鹿,實現(xiàn)了 Transform 以后,要想應(yīng)用蚜迅,就調(diào)用project.android.registerTransform()
public class MyTransform extends Transform {
@Override
public String getName() {
// 返回 transform 的名稱舵匾,最終的名稱會是 transformClassesWithMyTransformForDebug 這種形式
return "MyTransform";
}
@Override
public Set<QualifiedContent.ContentType> getInputTypes() {
/**
返回需要處理的數(shù)據(jù)類型 有 下面幾種類型可選
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() {
/**
返回需要處理內(nèi)容的范圍,有下面幾種類型
PROJECT(1), 只處理項目的內(nèi)容
SUB_PROJECTS(4), 只處理子項目
EXTERNAL_LIBRARIES(16), 只處理外部庫
TESTED_CODE(32), 只處理當前 variant 對應(yīng)的測試代碼
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刹帕,會進行全量編譯吵血,刪除上一次的輸出內(nèi)容
return false;
}
@Override
void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
// 在這里處理 class
super.transform(transformInvocation)
// 在 transform 里,如果沒有任何修改偷溺,也要把 input 的內(nèi)容輸出到 output蹋辅,否則會報錯
for (TransformInput input : transformInvocation.inputs) {
input.directoryInputs.each { dir ->
// 獲取對應(yīng)的輸出目錄
File output = transformInvocation.outputProvider.getContentLocation(dir.name, dir.contentTypes, dir.scopes, Format.DIRECTORY)
dir.changedFiles // 增量模式下修改的文件
dir.file // 獲取輸入的目錄
FileUtils.copyDirectory(dir.file, output) // input 內(nèi)容輸出到 output
}
input.jarInputs.each { jar ->
// 獲取對應(yīng)的輸出 jar
File output = transformInvocation.outputProvider.getContentLocation(jar.name, jar.contentTypes, jar.scopes, Format.JAR)
jar.file // 獲取輸入的 jar 文件
FileUtils.copyFile(jar.file, output) // input 內(nèi)容輸出到 output
}
}
}
}
// 注冊 transform
android.registerTransform(new MyTransform())
三、插件開發(fā)
在 android studio 中創(chuàng)建一個 java module
在 src/main 目錄下創(chuàng)建 groovy 目錄挫掏,然后創(chuàng)建自己的包名和插件類
-
在 src/main 目錄下創(chuàng)建 resources/META-INFO/gradle-plugins 目錄侦另,創(chuàng)建 myplugin.properties 文件,文件里內(nèi)容是
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' }
-
創(chuàng)建Plugin類
創(chuàng)建插件類尉共,就可以寫插件的代碼了褒傅。插件類繼承 Plugin,并實現(xiàn) apply 接口爸邢,apply 就是在 build.gradle 里 apply plugin 'xxx' 的時候要調(diào)用的接口了
插件開發(fā)可以使用 groovy 和 java樊卓,使用 groovy 的話可以有更多的語法糖,開發(fā)起來更方便一些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") } }
-
創(chuàng)建插件的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) } }
-
本地安裝插件
這樣一個簡單的插件就開發(fā)好了浇辜,如何使用呢
我們首先需要在 build.gradle 中引入 maven 插件券敌,并且配置 install 相關(guān)的屬性apply plugin: 'maven' install { repositories.mavenInstaller { pom.version = '0.0.1' // 配置插件版本號 pom.artifactId = 'myplugin' // 配置插件標識 pom.groupId = 'com.binzi.plugin' // 配置插件組織 } }
之后執(zhí)行 ./gradlew install 便會把插件安裝在本地 maven 倉庫
之后在使用的地方引入我們插件的 classpathclasspath 'com.binzi.plugin:myplugin:0.0.1'
加載插件
apply plugin; 'myplugin' // 這里的 myplugin 是前面說的 myplugin.properties 的名字
-
打包發(fā)布
在插件 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 文件就可以熊镣,內(nèi)容如下:
implementation-class=com.android.build.gradle.AppPlugin
這里定義了入口是 AppPlugin卑雁,AppPlugin 繼承自AbstractAppPlugin,AbstractAppPlugin繼承自BasePlugin募书。
AbstractAppPlugin 里沒有做太多的操作,主要是重寫了 createTaskManager 和 createExtension测蹲,剩下的大部分工作還是在 BasePlugin 里做的莹捡。
BasePlugin中的的入口函數(shù)是apply(0
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
這里主要調(diào)用
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
這里主要調(diào)用
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
這里主要調(diào)用basePluginApply和pluginSpecificApply,其中pluginSpecificApply是個抽象函數(shù)扣甲,在pluginSpecificApply中是個空實現(xiàn)篮赢,主要操作都在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);
//創(chuàng)建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();
//配置項對應(yīng)的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);
}
//創(chuàng)建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());
//應(yīng)用java插件
project.getPlugins().apply(JavaBasePlugin.class);
DslScopeImpl dslScope =
new DslScopeImpl(
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getDeprecationReporter(),
objectFactory);
//創(chuàng)建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監(jiān)聽
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);
}
其中主要是調(diào)用 createExtension來加載gradle腳本中的配置信息琉挖,在使用解析完后的配置信息創(chuàng)建taskManager启泣、variantManager等其他組件。
創(chuàng)建 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 的調(diào)用時機是在 project.afterEvaluate 里調(diào)用的示辈,這個時候所有模塊配置已經(jīng)完成了
在 BasePlugin.createAndroidTasks 里寥茫,是調(diào)用 VariantManager.createAndroidTasks 完成工作的。
四矾麻、Android Plugin主要 Task
1坠敷、Android 打包流程
官方介紹的流程如下:
- 編譯器將您的源代碼轉(zhuǎn)換成 DEX(Dalvik Executable) 文件(其中包括 Android 設(shè)備上運行的字節(jié)碼),將所有其他內(nèi)容轉(zhuǎn)換成已編譯資源射富。
- APK 打包器將 DEX 文件和已編譯資源合并成單個 APK膝迎。 不過,必須先簽署 APK胰耗,才能將應(yīng)用安裝并部署到 Android 設(shè)備上限次。
- APK 打包器使用調(diào)試或發(fā)布密鑰庫簽署您的 APK:
- 在生成最終 APK 之前风钻,打包器會使用 zipalign 工具對應(yīng)用進行優(yōu)化瓮孙,減少其在設(shè)備上運行時占用的內(nèi)存。
首先我們看一下 打包一個 apk 需要哪些 task仅父。
在項目根目錄下執(zhí)行命令:
./gradlew assembleDebug --console=plain
看一下輸出結(jié)果
> 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 對應(yīng)實現(xiàn)類
我們先看看每個 task 都是做什么的赠群,以及其對應(yīng)的實現(xiàn)類羊始。
先回憶一下,我們在前面 android-gradle-plugin 主要流程分析里說到過查描,task 的實現(xiàn)可以在 TaskManager 里找到突委,創(chuàng)建 Task 的方法主要是兩個,TaskManager.createTasksBeforeEvaluate() 和 ApplicationTaskManager.createTasksForVariantScope()冬三,所以這些 task 的實現(xiàn)匀油,也在這兩個類里找就可以,下面列出了各個 task 的作用及實現(xiàn)類勾笆。
Task | 對應(yīng)實現(xiàn)類 | 作用 |
---|---|---|
preBuild | 空 task敌蚜,只做錨點使用 | |
preDebugBuild | 空 task,只做錨點使用窝爪,與 preBuild 區(qū)別是這個 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 了,節(jié)省時間 |
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叮阅,錨點 |