Android 探索之 Task 分析(五)
本文将会分析如下几个task
createReleaseCompatibleScreenManifests
processReleaseManifest
splitsDiscoveryTaskRelease
processReleaseResources
generateReleaseSources
javaPreCompileRelease
13.createReleaseCompatibleScreenManifests
我们有一步生成了apk_list.json,此处生成对应apk的manifest,并输出output.json
源码分析
//com.android.build.gradle.tasks.CompatibleScreensManifest
@TaskAction
public void generateAll() throws IOException {
// 1、遍历所有的density,通过generate方法产生相应的manifest文件
// process all outputs.
new BuildElements(
outputScope
.getApkDatas()
.stream()
.map(
apkInfo -> {
// 遍历 density 生成对应的manifest文件
File generatedManifest = generate(apkInfo);
return generatedManifest != null
? new BuildOutput(
VariantScope.TaskOutputType
.COMPATIBLE_SCREEN_MANIFEST,
apkInfo,
generatedManifest)
: null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList()))
// 将结果输出为output.json并保存
.save(outputFolder);
}
@Nullable
public File generate(ApkData apkData) {
FilterData densityFilter =
apkData.getFilter(com.android.build.OutputFile.FilterType.DENSITY);
if (densityFilter == null) {
return null;
}
StringBuilder content = new StringBuilder();
content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
.append("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n")
.append(" package=\"${packageName}\">\n")
.append("\n");
if (minSdkVersion.get() != null) {
content.append(" <uses-sdk android:minSdkVersion=\"")
.append(minSdkVersion.get())
.append("\"/>\n");
}
content.append(" <compatible-screens>\n");
// convert unsupported values to numbers.
String density = convert(densityFilter.getIdentifier(), Density.XXHIGH, Density.XXXHIGH);
for (String size : getScreenSizes()) {
content.append(
" <screen android:screenSize=\"").append(size).append("\" "
+ "android:screenDensity=\"").append(density).append("\" />\n");
}
content.append(
" </compatible-screens>\n" +
"</manifest>");
File splitFolder = new File(outputFolder, apkData.getDirName());
FileUtils.mkdirs(splitFolder);
File manifestFile = new File(splitFolder, SdkConstants.ANDROID_MANIFEST_XML);
try {
Files.write(content.toString(), manifestFile, Charsets.UTF_8);
} catch (IOException e) {
throw new BuildException(e.getMessage(), e);
}
return manifestFile;
}
输入输出
-----task begin-------->
project: project ':app_driver'
name: createReleaseCompatibleScreenManifests
group: null
description: null
conv: [:]
inputs:
outputs:
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/manifests/density/release
<------task end -------
示例
参考文档:
Apk 拆分机制
Split_Config_google
build.gradle配置如下内容
splits {
// Screen density split settings
density {
// Enable or disable the density split mechanism
enable true
// Exclude these densities from splits
exclude "ldpi", "tvdpi", "xxhdpi", "xxxhdpi"
compatibleScreens 'small', 'normal', 'large', 'xlarge'
}
}
会生成如下的内容:
其中关于生成的manifest也可以手动配置,主要是为了google play 使用
具体详细分析可以参考https://blog.csdn.net/think_soft/article/details/7559563
生成的output.json文件类似如下:
[
{
"outputType": {
"type": "COMPATIBLE_SCREEN_MANIFEST"
},
"apkInfo": {
"type": "FULL_SPLIT",
"splits": [
{
"filterType": "DENSITY",
"value": "mdpi"
}
],
"versionCode": 1,
"versionName": "1.0",
"enabled": true,
"filterName": "mdpi",
"outputFile": "app-mdpi-release-unsigned.apk",
"fullName": "mdpiRelease",
"baseName": "mdpi-release"
},
"path": "mdpi/AndroidManifest.xml",
"properties": {}
},
{
"outputType": {
"type": "COMPATIBLE_SCREEN_MANIFEST"
},
"apkInfo": {
"type": "FULL_SPLIT",
"splits": [
{
"filterType": "DENSITY",
"value": "hdpi"
}
],
"versionCode": 1,
"versionName": "1.0",
"enabled": true,
"filterName": "hdpi",
"outputFile": "app-hdpi-release-unsigned.apk",
"fullName": "hdpiRelease",
"baseName": "hdpi-release"
},
"path": "hdpi/AndroidManifest.xml",
"properties": {}
},
{
"outputType": {
"type": "COMPATIBLE_SCREEN_MANIFEST"
},
"apkInfo": {
"type": "FULL_SPLIT",
"splits": [
{
"filterType": "DENSITY",
"value": "xhdpi"
}
],
"versionCode": 1,
"versionName": "1.0",
"enabled": true,
"filterName": "xhdpi",
"outputFile": "app-xhdpi-release-unsigned.apk",
"fullName": "xhdpiRelease",
"baseName": "xhdpi-release"
},
"path": "xhdpi/AndroidManifest.xml",
"properties": {}
}
]
14.processReleaseManifest
processReleaseManifest任务实际上做的是manifest文件的merge操作,将三种输入类型的manifest合成一个manifest文件,并生成相应操作日志文件。
源码分析
//com.android.build.gradle.tasks.MergeManifests
// 主要逻辑
//MergeManifest.java中的doFullTaskAction()方法。
@Override
protected void doFullTaskAction() throws IOException {
// 1.获取compatibleScreenManifests生成的manifest集合
// read the output of the compatible screen manifest.
BuildElements compatibleScreenManifests =
ExistingBuildElements.from(
VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST,
compatibleScreensManifest);
//2、重新设置packageName
String packageOverride;
if (packageManifest != null && !packageManifest.isEmpty()) {
packageOverride =
ApplicationId.load(packageManifest.getSingleFile()).getApplicationId();
} else {
packageOverride = getPackageOverride();
}
@Nullable BuildOutput compatibleScreenManifestForSplit;
ImmutableList.Builder<BuildOutput> mergedManifestOutputs = ImmutableList.builder();
ImmutableList.Builder<BuildOutput> irMergedManifestOutputs = ImmutableList.builder();
// FIX ME : multi threading.
// TODO : LOAD the APK_LIST FILE .....
//3.遍历apkDatas->上一步的split过程生成的apkdata
for (ApkData apkData : outputScope.getApkDatas()) {
//4.指定分辨率的buildOutput对象
compatibleScreenManifestForSplit = compatibleScreenManifests.element(apkData);
//5、正常模式内容的输出 full/release目录下
File manifestOutputFile =
FileUtils.join(
getManifestOutputDirectory(),
apkData.getDirName(),
SdkConstants.ANDROID_MANIFEST_XML);
//6、 instantRun模式的输出
File instantRunManifestOutputFile =
FileUtils.join(
getInstantRunManifestOutputDirectory(),
apkData.getDirName(),
SdkConstants.ANDROID_MANIFEST_XML);
//7、 生成mergingreport对象
MergingReport mergingReport =
getBuilder()
.mergeManifestsForApplication(
getMainManifest(),
getManifestOverlays(),
computeFullProviderList(compatibleScreenManifestForSplit),
getNavigationFiles(),
getFeatureName(),
packageOverride,
apkData.getVersionCode(),
apkData.getVersionName(),
getMinSdkVersion(),
getTargetSdkVersion(),
getMaxSdkVersion(),
manifestOutputFile.getAbsolutePath(),
// no aapt friendly merged manifest file necessary for applications.
null /* aaptFriendlyManifestOutputFile */,
instantRunManifestOutputFile.getAbsolutePath(),
ManifestMerger2.MergeType.APPLICATION,
variantConfiguration.getManifestPlaceholders(),
getOptionalFeatures(),
getReportFile());
XmlDocument mergedXmlDocument =
mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
ImmutableMap<String, String> properties =
mergedXmlDocument != null
? ImmutableMap.of(
"packageId",
mergedXmlDocument.getPackageName(),
"split",
mergedXmlDocument.getSplitName(),
SdkConstants.ATTR_MIN_SDK_VERSION,
mergedXmlDocument.getMinSdkVersion())
: ImmutableMap.of();
mergedManifestOutputs.add(
new BuildOutput(
VariantScope.TaskOutputType.MERGED_MANIFESTS,
apkData,
manifestOutputFile,
properties));
irMergedManifestOutputs.add(
new BuildOutput(
VariantScope.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS,
apkData,
instantRunManifestOutputFile,
properties));
}
// 8、存储full模式下的output.json
new BuildElements(mergedManifestOutputs.build()).save(getManifestOutputDirectory());
// 9、存储instantRun模式下的output.json文件
new BuildElements(irMergedManifestOutputs.build())
.save(getInstantRunManifestOutputDirectory());
}
第一步:从`createReleaseCompatibleScreenManifests'任务的输出读取,生成compatibleScreenManifests集合。所以这里的值有三个(hdpi、mdpi、xhdpi)BuildOutput对象;
第二步:重新设置packageName。如果我们同时在Manifest.xml和gradle里面同时设置packageId,且id值不同,则会在此用gradle里面的package覆盖Manifest.xml里面的id值。
第三步:遍历apkDatas->上一步的split过程生成的apkdata
第四步:进入不同的split遍历,得到指定分辨率的BuildOutput对象;
第五步:正常的manifest输出文件。(/app/build/intermediates/manifests/full/release/universal/AndroidManifest.xml)
第六步:instantRun模式下的Manifest输出。
第七步:生成mergingReport对象,调用的是AndroidBuilder中的mergeManifestsForApplication()方法,该方法会生成/output/logs/manifest-merger-release-report.txt
第八步:存储full模式下的output.json文件;
第九步:存储instantRun模式下的output.json文件
输入输出
-----task begin-------->
project: project ':app_driver'
name: processReleaseManifest
group: null
description: null
conv: [:]
inputs:
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/splits-support/release/apk-list/apk-list.gson
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/manifests/density/release
/Users/dongkai/Code/XiWeiLogistics/app_driver/src/main/AndroidManifest.xml
/Users/dongkai/Code/XiWeiLogistics/ymm_app_driver_main_module/build/intermediates/manifests/full/release/AndroidManifest.xml
...
...
...
/Users/dongkai/.gradle/caches/transforms-1/files-1.1/vivo-1.0.4.aar/8865158a811f23e9d9b6545f5ff48d01/AndroidManifest.xml
/Users/dongkai/.gradle/caches/transforms-1/files-1.1/mmkv-static-1.0.17.aar/0c39c42d42d3b884ed1b1cdbff217b58/AndroidManifest.xml
outputs:
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/manifests/instant-run/release
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/manifests/full/release
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/outputs/logs/manifest-merger-release-report.txt
<------task end -------
根据input file路径,可知输入分为三种类型:
- createReleaseCompatibleScreenManifests 任务生成的density规则(intermediates/manifests/density/release);
- 项目的manifest文件(/app/src/main/AndroidManifest.xml);
- 依赖的aar包的manifest和运行环境的manifest文件(appcompat-v7-26.1.0.aar/.../AndroidManifest.xml,runtime-1.0.0.aar/.../AndroidManifest.xml)。
根据output file路径,可知输出文件也为三种:
- instant-run模式下的输出(manifests/instant-run/release);
- 正常模式下的输出(manifests/full/release);
- merge manifest 的log记录(outputs/logs/manifest-merger-release-report.txt);
15.splitsDiscoveryTaskRelease
源码分析
//com.android.build.gradle.tasks.SplitsDiscovery
Set<File> mergedResourcesFolderFiles =
mergedResourcesFolders != null ? mergedResourcesFolders.getFiles() : null;
Collection<String> resConfigs = resourceConfigs;
if (resConfigAuto) {
resConfigs = discoverListOfResourceConfigsNotDensities();
}
SplitList.save(
getPersistedList(),
getFilters(mergedResourcesFolderFiles, DiscoverableFilterType.DENSITY),
getFilters(mergedResourcesFolderFiles, DiscoverableFilterType.LANGUAGE),
// no need to pass the source folders, we don't support Auto for ABI splits so far.
getFilters(ImmutableList.of(), DiscoverableFilterType.ABI),
resConfigs.stream().map(SplitList.Filter::new).collect(Collectors.toList()));
输入输出
-----task begin-------->
project: project ':app_driver'
name: splitsDiscoveryTaskRelease
group: null
description: null
conv: [:]
inputs:
outputs:
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/splits-support/release/split-list/split-list.gson
<------task end -------
输出文件类似如下结构
通过输出json文件,基本可以才到splitsDiscoveryTaskRelease任务就是实现对DENSITY、LANGUAGE、ABI和resConfig配置的存储,即生成split-list.json.
[
{
"splitType": "DENSITY",
"filters": [
{
"value": "mdpi"
},
{
"value": "hdpi"
},
{
"value": "xhdpi"
}
]
},
{
"splitType": "LANGUAGE",
"filters": []
},
{
"splitType": "ABI",
"filters": []
},
{
"splitType": "ResConfigs",
"filters": []
}
]
16.processReleaseResources
源码分析
// com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask
@Override
protected void doFullTaskAction() throws IOException {
WaitableExecutor executor = WaitableExecutor.useGlobalSharedThreadPool();
BuildElements manifestBuildElements =
ExistingBuildElements.from(taskInputType, manifestFiles);
final Set<File> packageIdFileSet =
packageIdsFiles != null
? packageIdsFiles.getArtifactFiles().getAsFileTree().getFiles()
: null;
final Set<File> featureResourcePackages = this.featureResourcePackages.getFiles();
SplitList splitList =
splitListInput == null ? SplitList.EMPTY : SplitList.load(splitListInput);
Set<File> dependencies =
dependenciesFileCollection != null
? dependenciesFileCollection.getFiles()
: Collections.emptySet();
Set<File> imports =
sharedLibraryDependencies != null
? sharedLibraryDependencies.getFiles()
: Collections.emptySet();
ImmutableList.Builder<BuildOutput> buildOutputs = ImmutableList.builder();
try (Aapt aapt = makeAapt()) {
// do a first pass at the list so we generate the code synchronously since it's required
// by the full splits asynchronous processing below.
List<BuildOutput> unprocessedManifest =
manifestBuildElements.stream().collect(Collectors.toList());
// 遍历所有的 split
for (BuildOutput manifestBuildOutput : manifestBuildElements) {
ApkInfo apkInfo = manifestBuildOutput.getApkInfo();
boolean codeGen =
(apkInfo.getType() == OutputFile.OutputType.MAIN
|| apkInfo.getFilter(OutputFile.FilterType.DENSITY) == null);
if (codeGen) {
// 这里只处理 main 和不依赖 density 的资源,此task 可以生成R.java文件
unprocessedManifest.remove(manifestBuildOutput);
buildOutputs.add(
invokeAaptForSplit(
manifestBuildOutput,
dependencies,
imports,
packageIdFileSet,
splitList,
featureResourcePackages,
apkInfo,
true, // 是否生成R文件
aapt));
break;
}
}
// 异步处理剩下的其他split,无需生成R.java文件
// now all remaining splits will be generated asynchronously.
for (BuildOutput manifestBuildOutput : unprocessedManifest) {
ApkInfo apkInfo = manifestBuildOutput.getApkInfo();
if (apkInfo.requiresAapt()) {
executor.execute(
() ->
invokeAaptForSplit(
manifestBuildOutput,
dependencies,
imports,
packageIdFileSet,
splitList,
featureResourcePackages,
apkInfo,
false,
aapt));
}
}
// 等待所有task完成
List<WaitableExecutor.TaskResult<BuildOutput>> taskResults = executor.waitForAllTasks();
taskResults.forEach(
taskResult -> {
if (taskResult.getException() != null) {
throw new BuildException(
taskResult.getException().getMessage(),
taskResult.getException());
} else {
buildOutputs.add(taskResult.getValue());
}
});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
if (multiOutputPolicy == MultiOutputPolicy.SPLITS) {
//...... 这一大串 普通app用不到,一般我们用的是Full模式,而不是split模式
}
// and save the metadata file.
new BuildElements(buildOutputs.build()).save(resPackageOutputFolder);
}
核心代码如下:
void invokeAaptForSplit(
Collection<BuildOutput> manifestsOutputs,
@NonNull Set<File> dependencySymbolTableFiles,
@Nullable Set<File> packageIdFileSet,
@NonNull SplitList splitList,
@NonNull Set<File> featureResourcePackages,
ApkData apkData,
boolean generateCode,
@Nullable Aapt aapt)
throws IOException {
// *.ap_ 文件
File resOutBaseNameFile =
new File(
resPackageOutputFolder,
FN_RES_BASE
+ RES_QUALIFIER_SEP
+ apkData.getFullName()
+ SdkConstants.DOT_RES);
...
// manifest file
File manifestFile = manifestOutput.getOutputFile();
...
// 生产相应的文件
getBuilder().processResources(aapt, config);
}
根据输入文件,生成resoure(*.ap_)文件和相应的R文件。
输入输出
-----task begin-------->
project: project ':app_driver'
name: processReleaseResources
group: null
description: null
conv: [:]
inputs:
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/splits-support/release/apk-list/apk-list.gson
/Users/dongkai/Code/XiWeiLogistics/ymm_app_driver_main_module/build/intermediates/res/symbol-table-with-package/release/package-aware-r.txt
...
...
...
/Users/dongkai/.gradle/caches/transforms-1/files-1.1/0c39c42d42d3b884ed1b1cdbff217b58/8696c64a66460929c73f0f3bd4321f4e/package-aware-r.txt
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/res/merged/release
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/manifests/full/release
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/splits-support/release/split-list/split-list.gson
outputs:
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/incremental/processReleaseResources
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/multi-dex/release/manifest_keep.txt
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/proguard-rules/release/aapt_rules.txt
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/res/release
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/generated/source/r/release
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/res/symbol-table-with-package/release/package-aware-r.txt
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/symbols/release/R.txt
<------task end -------
首先看下输入目录类型:
- mergeReleaseResources 任务的输出目录,intermediates/res/merged/release该文件夹下是经过aapt处理过的flat文件;
- processReleaseManifest 任务的输出目录,intermediates/manifests/full/release该文件夹下是生成的manifest.xml文件;
- splitsDiscoveryTaskRelease任务输出的目录;
- 各个依赖包的cache目录中的package-aware-r.txt文件,该文件里面的内容是相应包的资源的映射,类似如下:
android.support.v7.appcompat
int anim abc_fade_in 0x7f010001
int anim abc_fade_out 0x7f010002
int anim abc_grow_fade_in_from_bottom 0x7f010003
int anim abc_popup_enter 0x7f010004
第一行是包名,后面是资源的映射关系。
再来看下输出目录:
- /intermediates/incremental/processReleaseResources
- /build/intermediates/multi-dex/release/manifest_keep.txt 后续混淆的时候会用到
- /build/intermediates/proguard-rules/release/aapt_rules.txt aapt 混淆规则
- /intermediates/res/release该目录下主要是resources.ap_文件,该文件实际就是资源通过aapt后生成的资源压缩文件(解压后会发现里面就是:AndroidManifest.xml、res文件,以及resoures.arsc);
- /generated/source/r/release该目录也就是R.java文件。
- symbol-table-with-package/release/package-aware-r.txt生成项目的R文件的映射关系;
- intermediates/symbols/release/R.txt生成R文件的txt文件。
17.generateReleaseSources
空实现 锚task,无输入输出
源码分析
输入输出
-----task begin-------->
project: project ':app_driver'
name: generateReleaseSources
group: null
description: null
conv: [:]
inputs:
outputs:
<------task end -------
18.javaPreCompileRelease
源码分析
//com.android.build.gradle.tasks.JavaPreCompileTask
@TaskAction
public void preCompile() throws IOException {
boolean grandfathered = annotationProcessorOptions.getIncludeCompileClasspath() != null;
Collection<ResolvedArtifactResult> compileProcessors = null;
if (!grandfathered) {
compileProcessors = collectAnnotationProcessors(compileClasspaths);
FileCollection annotationProcessors =
annotationProcessorConfiguration.getArtifactFiles();
compileProcessors =
compileProcessors
.stream()
.filter(artifact -> !annotationProcessors.contains(artifact.getFile()))
.collect(Collectors.toList());
if (!compileProcessors.isEmpty()) {
String message =
"Annotation processors must be explicitly declared now. The following "
+ "dependencies on the compile classpath are found to contain "
+ "annotation processor. Please add them to the "
+ annotationProcessorConfigurationName
+ " configuration.\n - "
+ Joiner.on("\n - ")
.join(convertArtifactsToNames(compileProcessors))
+ "\nAlternatively, set "
+ "android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true "
+ "to continue with previous behavior. Note that this option "
+ "is deprecated and will be removed in the future.\n"
+ "See "
+ "https://developer.android.com/r/tools/annotation-processor-error-message.html "
+ "for more details.";
if (isForTesting) {
getLogger().warn(message);
} else {
throw new RuntimeException(message);
}
}
}
// Get all the annotation processors for metrics collection.
Set<String> classNames = Sets.newHashSet();
// Add the annotation processors on classpath only when includeCompileClasspath is true.
if (Boolean.TRUE.equals(annotationProcessorOptions.getIncludeCompileClasspath())) {
if (compileProcessors == null) {
compileProcessors = collectAnnotationProcessors(compileClasspaths);
}
classNames.addAll(convertArtifactsToNames(compileProcessors));
}
// Add all annotation processors on the annotation processor configuration.
classNames.addAll(
convertArtifactsToNames(
collectAnnotationProcessors(annotationProcessorConfiguration)));
// Add the explicitly declared processors.
// For metrics purposes, we don't care how they include the processor in their build.
classNames.addAll(annotationProcessorOptions.getClassNames());
// Add a generic reference to data binding, if present.
if (dataBindingEnabled) {
classNames.add(DATA_BINDING_SPEC);
}
FileUtils.deleteIfExists(processorListFile);
Gson gson = new GsonBuilder().create();
try (FileWriter writer = new FileWriter(processorListFile)) {
gson.toJson(classNames, writer);
}
}
输入输出
-----task begin-------->
project: project ':app_driver'
name: javaPreCompileRelease
group: null
description: null
conv: [:]
inputs:
/Users/dongkai/Code/XiWeiLogistics/ymm_app_driver_main_module/build/intermediates/intermediate-jars/release/classes.jar
/Users/dongkai/Code/XiWeiLogistics/ymm_app_driver_main_module/libs/yunceng.jar
/Users/dongkai/.gradle/caches/transforms-1/files-1.1/activity_result_util-1.0.1.aar/4b6d13d6fe7210d069c29939ba8fea95/jars/classes.jar
...
...
...
/Users/dongkai/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar
/Users/dongkai/.gradle/caches/transforms-1/files-1.1/mmkv-static-1.0.17.aar/0c39c42d42d3b884ed1b1cdbff217b58/jars/classes.jar
outputs:
/Users/dongkai/Code/XiWeiLogistics/app_driver/build/intermediates/javaPrecompile/release/annotationProcessors.json
<------task end -------
所有的输入均为项目直接或者间接引用到的库的jar包,
输出是一个annotationProcessors.json文件。文件类似如下:
["butterknife-compiler.jar (com.jakewharton:butterknife-compiler:9.0.0-rc2)"]
如果root 的build.gradle 使用了annotationProcessor则会产生一个映射