Android构建流程——篇八
文章目錄
- Task29 checkDebugLibraries
- 1. inut/ouput
- 2. 核心類(CheckMultiApkLibrariesTask)
- Task30 processDebugJavaRes
- 1. 預(yù)操作
- 1. input/ouput
- 核心類(ProcessJavaResConfigAction, SYNC)
- Task31 transformResourcesWithMergeJavaResForDebug
- 1. input/ouput
- 2. 核心類(MergeJavaResourcesTransform)
- Task32 validateSigningDebug
- 核心類(ValidateSigningTask)
- Task 33 packageDebug
- 1. input/ouput
- 2. 核心類(PackageApplication)
Task29 checkDebugLibraries
1. inut/ouput
taskName:checkDebugLibraries ========================================================= output:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/check-libraries/debug這個(gè)任務(wù)就是用來(lái)做校驗(yàn)的,如果存在多個(gè)modules打包成相同庫(kù),則會(huì)提示報(bào)錯(cuò)
2. 核心類(CheckMultiApkLibrariesTask)
@TaskAction fun taskAction() {// Build a map of libraries to their corresponding modules. If two modules package the same// library, we will use the map to output a user-friendly error message.val map = mutableMapOf<String, MutableList<String>>()var found = falsefor (artifact in featureTransitiveDeps) {// Sanity check. This should never happen.if (artifact.id.componentIdentifier !is ProjectComponentIdentifier) {throw GradleException(artifact.id.componentIdentifier.displayName + " is not a Gradle project.")}val projectPath =(artifact.id.componentIdentifier as ProjectComponentIdentifier).projectPathif (artifact.file.isFile) {found = found || updateLibraryMap(artifact.file, projectPath, map)}}if (found) {// Build the error message. Sort map and projectPaths for consistency.val output = StringBuilder()for ((library, projectPaths) in map.toSortedMap()) {if (projectPaths.size > 1) {output.append(projectPaths.sorted().joinToString(prefix = "[", postfix = "]")).append(" all package the same library [$library].\n")}}throw GradleException(output.toString())}}private fun updateLibraryMap(file: File,projectPath: String,map: MutableMap<String, MutableList<String>> ): Boolean {var found = falsefor (library in Files.readLines(file, Charsets.UTF_8)) {val libraryWithoutVariant = library.substringBeforeLast("::")if (map.containsKey(libraryWithoutVariant)) {found = truemap[libraryWithoutVariant]?.add(projectPath)} else {map[libraryWithoutVariant] = mutableListOf(projectPath)}}return found }Task30 processDebugJavaRes
1. 預(yù)操作
在debug/main目錄新增resources添加文件
1. input/ouput
taskName:processDebugJavaRes input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/src/main/resources/main-resources.txt input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/src/debug/resources/debug-resources.txt ========================================================= output:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/sourceFolderJavaResources/debug可以看到該任務(wù)就是將app中的resources中文件copy而已
核心類(ProcessJavaResConfigAction, SYNC)
// ProcessJavaResConfigAction.java public void execute(@NonNull Sync processResources) {// 執(zhí)行預(yù)操作for (SourceProvider sourceProvider :scope.getVariantConfiguration().getSortedSourceProviders()) {processResources.from(((AndroidSourceSet) sourceProvider).getResources().getSourceFiles());}processResources.setDestinationDir(destinationDir);}復(fù)制操作其實(shí)是交給SYNC這個(gè)類處理的,也就是sync任務(wù),可以理解為是一個(gè)copy操作,只是可以通過(guò)preserve來(lái)指定目標(biāo)目錄那些可以刪除,那些可以保留
具體參考官方文檔https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Sync.html
Task31 transformResourcesWithMergeJavaResForDebug
1. input/ouput
taskName:transformResourcesWithMergeJavaResForDebug input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/libs/java-json.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/adapters-3.2.0.aar/bc960c7c2fdbe5a1b97481ebd16e7d07/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/library-3.2.0.aar/de8b548685f788612cb022c06f944e21/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/modules-2/files-2.1/com.android.databinding/baseLibrary/3.2.0/fb5f8492c36231104cd86feaefa723291504c0a6/baseLibrary-3.2.0.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/multidex-1.0.2.aar/bf674b553893d1ca7ec3045705c93ed8/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/3835acebdd7fa231db259f7088788607/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/dafb9764e639c30db91be97006e47787/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/e50a7ebe45a7183db73f76387f58eadf/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/68eceb6dacbf0dc308d62e2a91559369/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/137dca77f851b0e904bb098c1da042aa/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/c49cb8624209768bd4984b8fead22f48/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/4a854f4c19aeb19de51777bbccdc7f48/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/6c38555fbd7f9d3e1a9de701d85ad85e/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/445504c8690be77ae0900482a270f4e9/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/160cc69dee260ec7a028fa1574fd87ba/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/transforms-1/files-1.1/runtime-1.0.3.aar/b7c442e59ee627256a254a2a31de63ac/jars/classes.jar input:/Users/dingbaosheng/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.3/7d7f60c4783872861222166f6164215f8951c7b1/common-1.0.3.jar input:/Users/dingbaosheng/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar input:/Users/dingbaosheng/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar input:/Users/dingbaosheng/.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:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/sourceFolderJavaResources/debug/debug-resources.txt input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/sourceFolderJavaResources/debug/main-resources.txt ========================================================= output:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/incremental/debug-mergeJavaRes/zip-cache output:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/transforms/mergeJavaRes/debug這個(gè)任務(wù)就是合并java資源文件(注意:此處資源文件不是指layout,asset,dex等資源文件)而是指標(biāo)準(zhǔn)等Java資源
看下mergeJavaRes/debug/0.jar產(chǎn)物,解壓如下
可以看到文件如下
- META-INF記錄了App中注解處理器涉及的各庫(kù)版本
- 我們?cè)趓esources目錄添加的資源文件
- 如果jar中包含.java文件也會(huì)被包含打包
2. 核心類(MergeJavaResourcesTransform)
核心類和task28一樣,此處略過(guò)
Task32 validateSigningDebug
該任務(wù)就是用來(lái)驗(yàn)證deubug模式下keystore文件是否存在,如果不存在直接創(chuàng)建并保存,
因?yàn)槲覀僑ample工程并沒(méi)有配置sign屬性,所以打包默認(rèn)會(huì)使用debug-keystore進(jìn)行簽名
核心類(ValidateSigningTask)
/*** A Gradle Task to check that the keystore file is present for this variant's signing config.** If the keystore is the default debug keystore, it will be created if it is missing.** This task has no explicit inputs, but is forced to run if the signing config keystore file is* not present.*/ open class ValidateSigningTask : AndroidVariantTask() {/*** Output directory to allow this task to be up-to-date, despite the the signing config file* not being modelled directly as an input or an output.*/@get:OutputDirectory lateinit var dummyOutputDirectory: Fileprivate setprivate lateinit var signingConfig: SigningConfigprivate lateinit var defaultDebugKeystoreLocation: File@TaskAction@Throws(ExecutionException::class, IOException::class)fun validate() = when {signingConfig.storeFile == null -> throw InvalidUserDataException("""Keystore file not set for signing config ${signingConfig.name}""")isSigningConfigUsingTheDefaultDebugKeystore() ->/* Check if the debug keystore is being used rather than directly checking if italready exists. A "fast path" of returning true if the store file is present wouldallow one task to return while another validate task has only partially written thedefault debug keystore file, which could lead to confusing transient build errors. */createDefaultDebugKeystoreIfNeeded()signingConfig.storeFile?.isFile == true -> {/* Keystore file is present, allow the build to continue. */}else -> throw InvalidUserDataException("""Keystore file '${signingConfig.storeFile?.absolutePath}' """+ """not found for signing config '${signingConfig.name}'.""")}// 如果/Users/{USER_NAME}/.android/debug.keystore不存在,創(chuàng)建一個(gè)debug.keystore@Throws(ExecutionException::class, IOException::class)private fun createDefaultDebugKeystoreIfNeeded() {checkState(signingConfig.isSigningReady, "Debug signing config not ready.")if (!defaultDebugKeystoreLocation.parentFile.canWrite()) {throw IOException("""Unable to create debug keystore in """+ """${defaultDebugKeystoreLocation.parentFile.absolutePath} because it is not writable.""")}/* Synchronized file with multi process locking requires that the parent directory of thedefault debug keystore is present.It is created as part of KeystoreHelper.defaultDebugKeystoreLocation() */checkState(FileUtils.parentDirExists(defaultDebugKeystoreLocation),"Parent directory of the default debug keystore '%s' does not exist",defaultDebugKeystoreLocation)/* Creating the debug keystore is done with the multi process file locking,to avoid one validate signing task from exiting early while the keystore is in theprocess of being written.The keystore is not locked in the task input presence check or where it is used atapplication packaging.This is generally safe as the keystore is only automatically created,never automatically deleted. */SynchronizedFile.getInstanceWithMultiProcessLocking(defaultDebugKeystoreLocation).createIfAbsent { createDefaultDebugStore(it, this.logger) }}private fun isSigningConfigUsingTheDefaultDebugKeystore(): Boolean {return signingConfig.name == BuilderConstants.DEBUG &&signingConfig.keyAlias == DefaultSigningConfig.DEFAULT_ALIAS &&signingConfig.keyPassword == DefaultSigningConfig.DEFAULT_PASSWORD &&signingConfig.storePassword == DefaultSigningConfig.DEFAULT_PASSWORD &&signingConfig.storeType == KeyStore.getDefaultType() &&signingConfig.storeFile.isSameFile(defaultDebugKeystoreLocation)}... }Task 33 packageDebug
1. input/ouput
taskName:packageDebug input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/apk_list/debug/mainApkListPersistenceDebug/apk-list.gson input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/merged_assets/debug/mergeDebugAssets/out input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/transforms/dexMerger/debug/0 input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/transforms/mergeJavaRes/debug/0.jar input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/merged_manifests/debug/processDebugManifest/merged input:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/processed_res/debug/processDebugResources/out input:/Users/dingbaosheng/.android/debug.keystore ========================================================= output:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/intermediates/incremental/packageDebug/tmp output:/Users/dingbaosheng/work/mockuai/project/AndroidGradleTaskDemo/app/build/outputs/apk/debug這個(gè)任務(wù)就是打包任務(wù),把之前的任務(wù)產(chǎn)物作為輸入打包成一個(gè)apk,主要流程圖如下
2. 核心類(PackageApplication)
/** Task to package an Android application (APK). */ public class PackageApplication extends PackageAndroidArtifact { }可以看到PackageApplication繼承類PackageAndroidArtifact,任務(wù)入口集中在基類
//PackageAndroidArtifact.java @Override protected void doFullTaskAction() {// check that we don't have colliding output file namescheckFileNameUniqueness();ExistingBuildElements.from(getTaskInputType(), resourceFiles).transform((ApkInfo apkInfo, File inputFile) -> {try {return splitFullAction(apkInfo, inputFile);} catch (IOException e) {throw new BuildException(e.getMessage(), e);}}).into(getInternalArtifactType(), outputDirectory);}可以看到方法最終調(diào)用splitFullAction方法
public File splitFullAction(@NonNull ApkInfo apkData, @Nullable File processedResources)throws IOException {File incrementalDirForSplit = new File(getIncrementalFolder(), apkData.getFullName());/** Clear the intermediate build directory. We don't know if anything is in there and* since this is a full build, we don't want to get any interference from previous state.*/if (incrementalDirForSplit.exists()) {FileUtils.deleteDirectoryContents(incrementalDirForSplit);} else {FileUtils.mkdirs(incrementalDirForSplit);}File cacheByPathDir = new File(incrementalDirForSplit, ZIP_DIFF_CACHE_DIR);FileUtils.mkdirs(cacheByPathDir);FileCacheByPath cacheByPath = new FileCacheByPath(cacheByPathDir);/** Clear the cache to make sure we have do not do an incremental build.*/cacheByPath.clear();Set<File> androidResources = getAndroidResources(processedResources);FileUtils.mkdirs(outputDirectory);BuildOutput buildOutput = computeBuildOutputFile(apkData);File outputFile = buildOutput.getOutputFile();/** Additionally, make sure we have no previous package, if it exists.*/FileUtils.deleteIfExists(outputFile);final ImmutableMap<RelativeFile, FileStatus> updatedDex;final ImmutableMap<RelativeFile, FileStatus> updatedJavaResources;if (!hasFeatureDexFiles()) {// 1. 讀取dex文件(合并后的)updatedDex = IncrementalRelativeFileSets.fromZipsAndDirectories(getDexFolders());// 2. 加載標(biāo)準(zhǔn)java資源文件(eg:0.jar)updatedJavaResources = getJavaResourcesChanges();} else {// We reach this code if we're in a feature module and minification is enabled in the// base module. In this case, we want to use the classes.dex file from the base// module's DexSplitterTransform.checkNotNull(getFeatureDexFolder());updatedDex = IncrementalRelativeFileSets.fromZipsAndDirectories(getFeatureDexFolder());// For now, java resources are in the base apk, so we exclude them here (b/77546738)updatedJavaResources = ImmutableMap.of();}// 3. 加載merged后的assets文件ImmutableMap<RelativeFile, FileStatus> updatedAssets =IncrementalRelativeFileSets.fromZipsAndDirectories(assets.getFiles());// 4. 加載android資源庫(kù)文件(eg:resources-debug*.ap_)ImmutableMap<RelativeFile, FileStatus> updatedAndroidResources =IncrementalRelativeFileSets.fromZipsAndDirectories(androidResources);// 5. so相關(guān)文件ImmutableMap<RelativeFile, FileStatus> updatedJniResources =IncrementalRelativeFileSets.fromZipsAndDirectories(getJniFolders());// 6. 清合后的清單文件BuildElements manifestOutputs = ExistingBuildElements.from(manifestType, manifests);// 7. 打包成apk,最終是通過(guò)一個(gè)叫ApkZFileCreator類進(jìn)行打包的doTask(apkData,incrementalDirForSplit,outputFile,cacheByPath,manifestOutputs,updatedDex,updatedJavaResources,updatedAssets,updatedAndroidResources,updatedJniResources);/** Update the known files.*/KnownFilesSaveData saveData = KnownFilesSaveData.make(incrementalDirForSplit);saveData.setInputSet(updatedDex.keySet(), InputSet.DEX);saveData.setInputSet(updatedJavaResources.keySet(), InputSet.JAVA_RESOURCE);saveData.setInputSet(updatedAssets.keySet(), InputSet.ASSET);saveData.setInputSet(updatedAndroidResources.keySet(), InputSet.ANDROID_RESOURCE);saveData.setInputSet(updatedJniResources.keySet(), InputSet.NATIVE_RESOURCE);saveData.saveCurrentData();recordMetrics(outputFile, processedResources);return outputFile; }👇
Android構(gòu)建流程——上篇
總結(jié)
以上是生活随笔為你收集整理的Android构建流程——篇八的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android构建流程——篇七
- 下一篇: Android-DataBinding源