文章序號
- Android gradle打包涉及task源碼解析(一)準備工作
- Android gradle打包涉及task源碼解析(二)
- Android gradle打包涉及task源碼解析(三)
- Android gradle打包涉及task源碼解析(四)
- Android gradle打包涉及task源碼解析(五)
- Android gradle打包涉及task源碼解析(六)
此篇文章將分析如下7個task。
:app:mergeDebugJniLibFolders
:app:transformNativeLibsWithMergeJniLibsForDebug
:app:processDebugJavaRes NO-SOURCE
:app:transformResourcesWithMergeJavaResForDebug
:app:validateSigningDebug
:app:packageDebug
:app:assembleDebug
mergeDebugJniLibFolders
執行命令:
./gradlew mergeDebugJniLibFolders
- inputs&outputs
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/src/debug/jniLibs
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/src/main/jniLibs
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/incremental/mergeDebugJniLibFolders
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/jniLibs/debug
輸入文件也就是我們項目的jniLibs目錄。
輸出分為兩種:
增量編譯的目錄(intermediates/incremental/mergeDebugJniLibFolders
),暫不分析;
jniLibs目錄(intermediates/jniLibs/debug
)。
通過輸入輸出基本能判斷
mergeDebugJniLibFolders
任務就是把項目的jniLibs目錄合到intermediates/jniLibs/debug
下面。
- 源碼
- 主要代碼邏輯
看到
MergeSourceSetFolders
這個類是不是很熟悉?在介紹mergeDebugAssets
task時,有分析到這個類,這里就不在具體分析了,實際干的事情類似,就是將相應的目錄下的文件merge到一個指定的目錄下。
transformNativeLibsWithMergeJniLibsForDebug
執行命令:
./gradlew transformNativeLibsWithMergeJniLibsForDebug
- inputs&outputs
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/6b443e96f1af9aa241aaa70576c67a57/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f44da5c361a1f52801511229596f72e7/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/butterknife-8.5.1.aar/9d5de52440cb778daab09db33955642f/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/9c804d63d6f065a8f9945f9ad94fee0e/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/4e56cc34abf77378e2b8d16ee237c82d/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.jakewharton/butterknife-annotations/8.5.1/bb67dad90bab7cd77a8f7f1b8442b47e3a2326bc/butterknife-annotations-8.5.1.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/3bf8586900bd31e222ef8b68bfd6e744/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/77cf518e9868987a283f04cec221fefa/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/8634ab1afa6a5a1a947a7bd163aba14f/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/8902e2a864b44d47c26fbc80fdafe175/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.android.support.constraint/constraint-layout-solver/1.1.3/bde0667d7414c16ed62d3cfe993cff7f9d732373/constraint-layout-solver-1.1.3.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/ed085e7b9476f7a9fef4ffbb323166ba/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.0/e414a4cb28434e25c4f6aa71426eb20cf4874ae9/common-1.0.0.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/jniLibs/debug/armeabi-v7a/libcommon-jni.so
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/jniLibs/debug/armeabi/libcommon-jni.so
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/jniLibs/debug/GDTActionSDK.min.1.2.0.jar
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/incremental/debug-mergeJniLibs/zip-cache
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/transforms/mergeJniLibs/debug
查看輸入文件,會發現輸入文件類型分為兩種:
1、依賴的庫的jar文件;
2、mergeDebugJniLibFolders
任務的輸出。
輸出目錄也很簡單,一個是增量編譯的目錄,一個是真正的merge輸出目錄。
- 源碼
- 主要代碼邏輯
public void transform(@NonNull TransformInvocation invocation)
throws IOException, TransformException {
...
// 輸入inputs、output執行merge操作
state = IncrementalFileMerger.merge(ImmutableList.copyOf(inputs), output, state);
saveMergeState(state);
cacheUpdates.forEach(Runnable::run);
}
通過代碼可以很清晰的知道這個任務就是對輸入進行一個merge操作,merge的邏輯在IncrementalFileMerger類里面,這里不在具體分析。
MergeJavaResourcesTransform
這個類實際上負責多種類型的merge操作,通過類型區分,代碼如下:
if (mergedType == QualifiedContent.DefaultContentType.RESOURCES) {
acceptedPathsPredicate =
path -> !path.endsWith(SdkConstants.DOT_CLASS)
&& !path.endsWith(SdkConstants.DOT_NATIVE_LIBS);
} else if (mergedType == ExtendedContentType.NATIVE_LIBS) {
transformResourcesWithMergeJavaResForDebug
執行命令:
./gradlew transformResourcesWithMergeJavaResForDebug
- inputs&outputs
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/6b443e96f1af9aa241aaa70576c67a57/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f44da5c361a1f52801511229596f72e7/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/butterknife-8.5.1.aar/9d5de52440cb778daab09db33955642f/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.11.0/75966e05a49046ca2ae734e5626f28837a8d1e82/okhttp-3.11.0.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/9c804d63d6f065a8f9945f9ad94fee0e/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/4e56cc34abf77378e2b8d16ee237c82d/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.jakewharton/butterknife-annotations/8.5.1/bb67dad90bab7cd77a8f7f1b8442b47e3a2326bc/butterknife-annotations-8.5.1.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/3bf8586900bd31e222ef8b68bfd6e744/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/77cf518e9868987a283f04cec221fefa/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/8634ab1afa6a5a1a947a7bd163aba14f/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/8902e2a864b44d47c26fbc80fdafe175/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.android.support.constraint/constraint-layout-solver/1.1.3/bde0667d7414c16ed62d3cfe993cff7f9d732373/constraint-layout-solver-1.1.3.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.14.0/102d7be47241d781ef95f1581d414b0943053130/okio-1.14.0.jar
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/ed085e7b9476f7a9fef4ffbb323166ba/jars/classes.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.0/e414a4cb28434e25c4f6aa71426eb20cf4874ae9/common-1.0.0.jar
input file:/Users/chao.zheng/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/incremental/debug-mergeJavaRes/zip-cache
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/transforms/mergeJavaRes/debug
- 源碼
- 主要代碼邏輯
實際上和前一個task
transformNativeLibsWithMergeJniLibsForDebug
復用的是一套邏輯,只不過在merge的時候一個是lib類型,一個是resource類型。這個task就是將相應依賴的jar里面的resource合并到intermediates/transforms/mergeJavaRes/debug
目錄。但是有一點需要注意,merge java resource時會有些文件時默認不會執行merge操作的,如下:
public PackagingOptions() {
// ATTENTION - keep this in sync with JavaDoc above.
exclude("/META-INF/LICENSE");
exclude("/META-INF/LICENSE.txt");
exclude("/META-INF/MANIFEST.MF");
exclude("/META-INF/NOTICE");
exclude("/META-INF/NOTICE.txt");
exclude("/META-INF/*.DSA");
exclude("/META-INF/*.EC");
exclude("/META-INF/*.SF");
exclude("/META-INF/*.RSA");
exclude("/META-INF/maven/**");
exclude("/NOTICE");
exclude("/NOTICE.txt");
exclude("/LICENSE.txt");
exclude("/LICENSE");
// Exclude version control folders.
exclude("**/.svn/**");
exclude("**/CVS/**");
exclude("**/SCCS/**");
// Exclude hidden and backup files.
exclude("**/.*/**");
exclude("**/.*");
exclude("**/*~");
// Exclude index files
exclude("**/thumbs.db");
exclude("**/picasa.ini");
// Exclude javadoc files
exclude("**/about.html");
exclude("**/package.html");
exclude("**/overview.html");
// Exclude stuff for unknown reasons
exclude("**/_*");
exclude("**/_*/**");
// Merge services
merge("/META-INF/services/**");
}
以上的配置是默認不會執行merge操作的。當然這里gradle也提供了一些其他的配置如:
packagingOptions {
merge ""
exclude ""
pickFirsts ""
}
validateSigningDebug
- 源碼
- 主要代碼邏輯
看下這個類的注視
/**
* A validate task that creates the debug keystore if it's missing. It only creates it if it's in
* the default debug keystore location.
*
* It's linked to a given SigningConfig
*/
通過注視可以很明確的知道,這個task就是在沒有keystore時創建一個keystore,也就是在debug的時候,會使用默認的keystore,這也是為什么在debug模式下,沒有配置keystore時,apk仍然能夠安裝使用。
packageDebug
勝利在望,接下來分析最后一個task.
執行命令:./gradlew packageDebug
- inputs&outputs
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/assets/debug
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/transforms/dexMerger/debug/0
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/transforms/mergeJavaRes/debug/0.jar
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/transforms/mergeJniLibs/debug/0
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/full/debug
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/res/debug
input file:/Users/chao.zheng/.android/debug.keystore
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/incremental/packageDebug
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/outputs/apk/debug
分析下輸入目錄:
1、assets/debug
assets目錄下的文件;
2、dexMerger/debug/0
dex 文件;
3、mergeJavaRes/debug/0.jar
java resource 文件;
4、mergeJniLibs/debug/0
so文件;
5、manifests/full/debug
manifest文件;
6、res/debug
resources 文件;
7、debug.keystore
keystore 文件;
輸出文件即為apk文件。
所以packageDebug
任務就是打apk包的。
- 源碼
- 主要代碼邏輯
/**
* Packages the application incrementally. In case of instant run packaging, this is not a
* perfectly incremental task as some files are always rewritten even if no change has occurred.
*
* @param apkData the split being built
* @param outputFile expected output package file
* @param changedDex incremental dex packaging data
* @param changedJavaResources incremental java resources
* @param changedAssets incremental assets
* @param changedAndroidResources incremental Android resource
* @param changedNLibs incremental native libraries changed
* @throws IOException failed to package the APK
*/
private void doTask(
@NonNull ApkData apkData,
@NonNull File incrementalDirForSplit,
@NonNull File outputFile,
@NonNull FileCacheByPath cacheByPath,
@NonNull Collection<BuildOutput> manifestOutputs,
@NonNull ImmutableMap<RelativeFile, FileStatus> changedDex,
@NonNull ImmutableMap<RelativeFile, FileStatus> changedJavaResources,
@NonNull ImmutableMap<RelativeFile, FileStatus> changedAssets,
@NonNull ImmutableMap<RelativeFile, FileStatus> changedAndroidResources,
@NonNull ImmutableMap<RelativeFile, FileStatus> changedNLibs)
throws IOException {
ImmutableMap.Builder<RelativeFile, FileStatus> javaResourcesForApk =
ImmutableMap.builder();
javaResourcesForApk.putAll(changedJavaResources);
final ImmutableMap<RelativeFile, FileStatus> dexFilesToPackage = changedDex;
...
// 1、創建packager 對象
try (IncrementalPackager packager =
new IncrementalPackagerBuilder()
.withOutputFile(outputFile)
.withSigning(signingConfig)
.withCreatedBy(getBuilder().getCreatedBy())
.withMinSdk(getMinSdkVersion())
// TODO: allow extra metadata to be saved in the split scope to avoid
// reparsing
// these manifest files.
.withNativeLibraryPackagingMode(
PackagingUtils.getNativeLibrariesLibrariesPackagingMode(
manifestForSplit.getOutputFile()))
.withNoCompressPredicate(
PackagingUtils.getNoCompressPredicate(
aaptOptionsNoCompress, manifestForSplit.getOutputFile()))
.withIntermediateDir(incrementalDirForSplit)
.withProject(getProject())
.withDebuggableBuild(getDebugBuild())
.withAcceptedAbis(
abiFilter == null ? abiFilters : ImmutableSet.of(abiFilter))
.withJniDebuggableBuild(getJniDebugBuild())
.build()) {
// 2、更新package相應的內容
packager.updateDex(dexFilesToPackage);
packager.updateJavaResources(changedJavaResources);
packager.updateAssets(changedAssets);
packager.updateAndroidResources(changedAndroidResources);
packager.updateNativeLibraries(changedNLibs);
...
}
代碼也很簡單就是創建IncrementalPackager 對象packager,然后對dex、javares、assets、AndroidResources、NativeLib執行更新操作,也就是寫入操作。執行這些update操作,最終都是調用了IncrementalPackager種的update()方法。代碼如下:
/**
* Updates files in the archive.
*
* @param updates the updates to perform
* @throws IOException failed to update the archive
*/
private void updateFiles(@NonNull Set<PackagedFileUpdate> updates) throws IOException {
...
for (File arch : Sets.newHashSet(archives)) {
mApkCreator.writeZip(arch, pathNameMap::get, name -> !names.contains(name));
}
}
看最后一句
mApkCreator.writeZip()
,上面的update操作,都通過mAplCreator寫入了Apk包中。
至此在debug模式下生成apk的所有tasks都已經分析完畢。對系列文章能看到這里來的,我相信對這個android的編譯過程基本有了比較清晰的認識了。
基本的編譯過程分析完了,后續將再此系列文章基礎上再出兩個主體:1、《Gradle tool 3.0 增量編譯》;2、《MutilDex拆分過程詳細解析》。