文章序號
- 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"
}
}
接著執(zhí)行命令
./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文件和代碼中使用,方便一些動態(tài)化的配置。
generateDebugResources
很遺憾該任務的相關邏輯沒有找到,有知道的麻煩留言告知一下。
mergeDebugResources
執(zhí)行命令
./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、執(zhí)行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 對象。該類實現(xiàn)了DataMerger抽象類,主要的merge邏輯均在此類里面實現(xiàn),此處不再展開,有興趣的自行閱讀: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工具對象。
第七步:執(zhí)行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"
}
}
執(zhí)行命令:
./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
執(zhí)行命令:
./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規(guī)則(intermediates/manifests/density/debug);
2、項目的manifest文件(TasksPro/app/src/main/AndroidManifest.xml);
3、依賴的aar包的manifest和運行環(huán)境的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) {
...
// 執(zhí)行merge操作,調用的是ManifestMerger2類的merge()方法。
MergingReport mergingReport = manifestMergerInvoker.merge();
...
}
此任務的核心merge邏輯都在這一步,詳細的細節(jié)有興趣的同學自行看源碼: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文件,并生成相應操作日志文件。