文章序号
- Android gradle打包涉及task源码解析(一)准备工作
- Android gradle打包涉及task源码解析(二)
- Android gradle打包涉及task源码解析(三)
- Android gradle打包涉及task源码解析(四)
- Android gradle打包涉及task源码解析(五)
- Android gradle打包涉及task源码解析(六)
此篇文章将分析如下5个task。
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:createDebugCompatibleScreenManifests UP-TO-DATE
:app:processDebugManifest
generateDebugResValues
- 准备
在项目的app/gradle中添加如下代码
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// 添加代码
resValue "string", "AppName", "app_release"
}
// 添加代码
debug {
resValue "string", "AppName", "app_debug"
}
}
接着执行命令
./gradlew generateDebugResValues
- inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/resValues/debug
输出文件目录下面会生成
generated.xml
文件,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from build type: debug -->
<string name="AppName" translatable="false">app_debug</string>
</resources>
生成的内容我们可以直接在xml中使用(
android:text="@string/AppName"
),也可以在代码中使用(getResources().getString(R.string.AppName);
)。
- 源码
- 主要代码逻辑
GenerateResValues.java中的generate()方法
@TaskAction
void generate() throws IOException, ParserConfigurationException {
// 1、输出路径,就是我们output中的目录
File folder = getResOutputDir();
// 2、这里的getItems()就是获得我们在代码中设置的resValues
List<Object> resolvedItems = getItems();
if (resolvedItems.isEmpty()) {
FileUtils.cleanOutputDir(folder);
} else {
// 3、在resolvedItems不等于空的时候,通过ResValueGenerator生成我们的generated.xml文件
ResValueGenerator generator = new ResValueGenerator(folder);
generator.addItems(getItems());
generator.generate();
}
}
通过上面的分析,我们知道generateDebugResValues 任务就是把我们在gradle里面配置的resValue读取到,然后在
/build/generated/res/resValues/debug
目录下生成generate.xml
文件,该文件里面的内容,可直接在xml文件和代码中使用,方便一些动态化的配置。
generateDebugResources
很遗憾该任务的相关逻辑没有找到,有知道的麻烦留言告知一下。
mergeDebugResources
执行命令
./gradlew mergeDebugResources
- inputs&outputs
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/resValues/debug
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/a7cc521b4567369eba0ddb355f44a660/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/604f3f8b356720eda7bfab425c06a06e/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/500188dffd88c5be8587eb6372bbf06d/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/9806df9e60c4aacc7f9f357a91ad2e92/res
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/rs/debug
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/src/main/res
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/src/debug/res
---------------------------------------------------
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/blame/res/debug
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/pngs/debug
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/incremental/mergeDebugResources
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/res/merged/debug
根据输入文件路径,大致有这几种:
1、generateDebugResValues任务生成的resValues文件(generated/res/resValues/debug
);
2、引用的aar包里面的资源(appcompat-v7-26.1.0.aar/a7cc521b4567369eba0ddb355f44a660/res
,constraint-layout-1.1.3.aar/604f3f8b356720eda7bfab425c06a06e/res
);
3、compileDebugRenderscript 任务生的Renderscript文件(generated/res/rs/debug
);
4、项目中的res资源文件(TasksPro/app/src/main/res
、TasksPro/app/src/debug/res
);
输入文件经过
mergeDebugResources
任务处理后,生产的文件有如下几种:
1、png图片集合(generated/res/pngs/debug
);
2、merge后的资源集合(incremental/mergeDebugResources
);
3、资源映射关系集合(intermediates/res/merged/debug
);
4、merge操作的日志记录(intermediates/blame/res/debug
);
- 源码
- 主要代码逻辑
MergeResources.java->doFullTaskAction()方法
@Override
protected void doFullTaskAction() throws IOException, ExecutionException, JAXBException {
// 1、得到ResourcePreprocessor子类对象
ResourcePreprocessor preprocessor = getPreprocessor();
// 2、得到task的output目录
// this is full run, clean the previous output
File destinationDir = getOutputDir();
FileUtils.cleanOutputDir(destinationDir);
// 3、得到inputs文件目录集合
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);
// 4、生成ResourceMerger 对象
// create a new merger and populate it with the sets.
ResourceMerger merger = new ResourceMerger(minSdk);
// 5、mergeingLog 记录
MergingLog mergingLog =
getBlameLogFolder() != null ? new MergingLog(getBlameLogFolder()) : null;
// 6、resourceCompiler,实际是Appt工具对象
try (QueueableResourceCompiler resourceCompiler =
processResources
? makeAapt(
aaptGeneration,
getBuilder(),
fileCache,
crunchPng,
variantScope,
getAaptTempDir(),
mergingLog)
: QueueableResourceCompiler.NONE) {
for (ResourceSet resourceSet : resourceSets) {
resourceSet.loadFromFiles(getILogger());
merger.addDataSet(resourceSet);
}
MergedResourceWriter writer =
new MergedResourceWriter(
workerExecutorFacade,
destinationDir,
getPublicFile(),
mergingLog,
preprocessor,
resourceCompiler,
getIncrementalFolder(),
dataBindingLayoutProcessor,
mergedNotCompiledResourcesOutputDirectory,
pseudoLocalesEnabled,
getCrunchPng());
// 7、执行merge resource 操作
merger.mergeData(writer, false /*doCleanUp*/);
if (dataBindingLayoutProcessor != null) {
dataBindingLayoutProcessor.end();
}
// No exception? Write the known state.
merger.writeBlobTo(getIncrementalFolder(), writer, false);
} catch (MergingException e) {
System.out.println(e.getMessage());
merger.cleanBlob(getIncrementalFolder());
throw new ResourceException(e.getMessage(), e);
} finally {
cleanup();
}
}
第一步:得到ResourcePreprocessor子类对象,实际是
MergeResourcesVectorDrawableRenderer
对象,该类又继承了VectorDrawableRenderer
,该类如下:/** * Generates PNG images (and XML copies) from VectorDrawable files. */ public class VectorDrawableRenderer implements ResourcePreprocessor {
通过说明,可以知道该类是通过VectorDrawable文件生产PNG图片,或者拷贝 xml文件。
第二步:得到task的output目录,实际上就是
/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/res/merged/debug
目录;
第三步:得到inputs文件目录集合,调用
getConfiguredResourceSets(preprocessor)
方法,该方法就是得到inputs路径;
第四步:生成ResourceMerger 对象。该类实现了DataMerger抽象类,主要的merge逻辑均在此类里面实现,此处不再展开,有兴趣的自行阅读:https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java
第五步:mergeingLog 记录,就是
intermediates/blame/res/debug
目录,记录操作日志。
第六步:resourceCompiler,实际是Appt工具对象。
第七步:执行merge resource 操作
mergeDebugResources
任务是把依赖的库和工程中的资源进行merge操作。
createDebugCompatibleScreenManifests
- 准备(gradle splits配置)
在项目的app/gradle android模块下添加如下代码:
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"
}
}
执行命令:
./gradlew createDebugCompatibleScreenManifests
- inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/manifests/density/debug
在output目录下会生成
hdpi/AndroidManigest.xml
,mdpi/AndroidManigest.xml
,xhdpi/AndroidManigest.xml
和output.json文件。Manifest文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="">
<uses-sdk android:minSdkVersion="14"/>
<compatible-screens>
</compatible-screens>
</manifest>
output.json文件内容如下(经格式化处理的):
[{
"outputType": {
"type": "COMPATIBLE_SCREEN_MANIFEST"
},
"apkInfo": {
"type": "FULL_SPLIT",
"splits": [{
"filterType": "DENSITY",
"value": "xhdpi"
}],
"versionCode": 1
},
"path": "xhdpi/AndroidManifest.xml",
"properties": {}
}, {
"outputType": {
"type": "COMPATIBLE_SCREEN_MANIFEST"
},
"apkInfo": {
"type": "FULL_SPLIT",
"splits": [{
"filterType": "DENSITY",
"value": "hdpi"
}],
"versionCode": 1
},
"path": "hdpi/AndroidManifest.xml",
"properties": {}
}, {
"outputType": {
"type": "COMPATIBLE_SCREEN_MANIFEST"
},
"apkInfo": {
"type": "FULL_SPLIT",
"splits": [{
"filterType": "DENSITY",
"value": "mdpi"
}],
"versionCode": 1
},
"path": "mdpi/AndroidManifest.xml",
"properties": {}
}]
- 源码
- 主要代码逻辑
public void generateAll() throws IOException {
// 1、遍历所有的density,通过generate方法生产响应的manifest文件
// process all outputs.
outputScope.parallelForEach(
VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST, this::generate);
// 2、生成json文件
// now write the metadata file.
outputScope.save(
ImmutableList.of(VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST),
outputFolder);
}
@Nullable
public File generate(ApkData apkData) throws IOException {
String density = apkData.getFilter(com.android.build.OutputFile.FilterType.DENSITY);
if (density == 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=\"\">\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.
density = convert(density, 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);
Files.write(content.toString(), manifestFile, Charsets.UTF_8);
return manifestFile;
}
代码很简单第一步遍历density,然后生产相应的manifest文件。第二步生成output.json文件。
processDebugManifest
执行命令:
./gradlew processDebugManifest
- inputs&outputs
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/density/debug
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/src/main/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/6b443e96f1af9aa241aaa70576c67a57/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f44da5c361a1f52801511229596f72e7/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/9c804d63d6f065a8f9945f9ad94fee0e/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/4e56cc34abf77378e2b8d16ee237c82d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/3bf8586900bd31e222ef8b68bfd6e744/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/77cf518e9868987a283f04cec221fefa/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/8634ab1afa6a5a1a947a7bd163aba14f/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/8902e2a864b44d47c26fbc80fdafe175/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/ed085e7b9476f7a9fef4ffbb323166ba/AndroidManifest.xml
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/instant-run/debug
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/full/debug
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/outputs/logs/manifest-merger-debug-report.txt
根据input file路径,可知输入分为三种类型:
1、createDebugCompatibleScreenManifests 任务生成的density规则(intermediates/manifests/density/debug
);
2、项目的manifest文件(TasksPro/app/src/main/AndroidManifest.xml
);
3、依赖的aar包的manifest和运行环境的manifest文件(appcompat-v7-26.1.0.aar/.../AndroidManifest.xml
,runtime-1.0.0.aar/.../AndroidManifest.xml
)。
根据output file路径,可知输出文件也为三种:
1、instant-run模式下的输出(manifests/instant-run/debug
);
2、正常模式下的输出(manifests/full/debug
);
3、merge manifest 的log记录(outputs/logs/manifest-merger-debug-report.txt
);
- 源码
- 主要代码逻辑
MergeManifest.java中的doFullTaskAction()方法。
protected void doFullTaskAction() throws IOException {
// 1、生成compatibleScreenManifests集合
// read the output of the compatible screen manifest.
Collection<BuildOutput> compatibleScreenManifests =
BuildOutputs.load(
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;
// 3、根据splits生成ApkData集合
List<ApkData> splitsToGenerate =
ProcessAndroidResources.getApksToGenerate(
outputScope, supportedAbis, buildTargetAbi, buildTargetDensity);
// 4、遍历splitsToGenerate集合
// FIX ME : multi threading.
for (ApkData apkData : splitsToGenerate) {
// 5、指定分辨率的BuildOutput对象
compatibleScreenManifestForSplit =
OutputScope.getOutput(
compatibleScreenManifests,
VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST,
apkData);
// 6、正常的输出
File manifestOutputFile =
FileUtils.join(
getManifestOutputDirectory(),
apkData.getDirName(),
SdkConstants.ANDROID_MANIFEST_XML);
// 7、instantRun 模式输出
File instantRunManifestOutputFile =
FileUtils.join(
getInstantRunManifestOutputDirectory(),
apkData.getDirName(),
SdkConstants.ANDROID_MANIFEST_XML);
// 8、生成mergingReport对象
MergingReport mergingReport =
getBuilder()
.mergeManifestsForApplication(
getMainManifest(),
getManifestOverlays(),
computeFullProviderList(compatibleScreenManifestForSplit),
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();
outputScope.addOutputForSplit(
VariantScope.TaskOutputType.MERGED_MANIFESTS,
apkData,
manifestOutputFile,
properties);
outputScope.addOutputForSplit(
VariantScope.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS,
apkData,
instantRunManifestOutputFile,
properties);
}
// 9、存储full模式下的output.json文件
outputScope.save(
ImmutableList.of(VariantScope.TaskOutputType.MERGED_MANIFESTS),
getManifestOutputDirectory());
// 10、存储instantRun模式下的output.json文件
outputScope.save(
ImmutableList.of(VariantScope.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS),
getInstantRunManifestOutputDirectory());
}
第一步:从`createDebugCompatibleScreenManifests'任务的输出读取,生成compatibleScreenManifests集合。所以这里的值有三个(hdpi、mdpi、xhdpi)BuildOutput对象;
第二步:重新设置packageName。如果我们同时在Manifest.xml和gradle里面同时设置packageId,且id值不同,则会在此用gradle里面的package覆盖Manifest.xml里面的id值。
第三步:根据splits的配置,生成apkData集合splitsToGenerate对象;
第四步:遍历splitsToGenerate集合,生成不同的输出文件;
第五步:进入不同的split遍历,得到指定分辨率的BuildOutput对象;
第六步:正常的manifest输出文件。(
/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/full/debug/universal/AndroidManifest.xml
)
第七步:instantRun模式下的Manifest输出。
第八步:生成mergingReport对象,调用的是AndroidBuilder中的
mergeManifestsForApplication()
方法,该方法如下:
/** Invoke the Manifest Merger version 2. */
public MergingReport mergeManifestsForApplication(
@NonNull File mainManifest,
@NonNull List<File> manifestOverlays,
@NonNull List<? extends ManifestProvider> dependencies,
@Nullable String featureName,
String packageOverride,
int versionCode,
String versionName,
@Nullable String minSdkVersion,
@Nullable String targetSdkVersion,
@Nullable Integer maxSdkVersion,
@NonNull String outManifestLocation,
@Nullable String outAaptSafeManifestLocation,
@Nullable String outInstantRunManifestLocation,
ManifestMerger2.MergeType mergeType,
Map<String, Object> placeHolders,
@NonNull List<Invoker.Feature> optionalFeatures,
@Nullable File reportFile) {
...
// 执行merge操作,调用的是ManifestMerger2类的merge()方法。
MergingReport mergingReport = manifestMergerInvoker.merge();
...
}
此任务的核心merge逻辑都在这一步,详细的细节有兴趣的同学自行看源码:https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java?autodive=0%2F%2F%2F%2F
第九步:存储full模式下的output.json文件;
第十步:存储instantRun模式下的output.json文件
经过上面分析可知,
processDebugManifest
任务实际上做的是manifest文件的merge操作,将三种输入类型的manifest合成一个manifest文件,并生成相应操作日志文件。