Android构建流程——篇三
文章目錄
- Task5 checkDebugManifest
- 1. input/ouput
- 2. 核心類(CheckManifest)
- Task6 generateDebugBuildConfig
- 1. input/output
- 2. 核心類(GenerateBuildConfig)
- Task7 prepareLintJar
- 1. input/ouput
- 2. 核心類(PrepareLintJar)
- Task8 mainApkListPersistenceDebug
- 1. input/output
- 2. 核心類(MainApkListPersistence)
Task5 checkDebugManifest
1. input/ouput
taskName:checkDebugManifest ========================================================= output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/check-manifest/debug2. 核心類(CheckManifest)
@TaskAction void check() {if (!isOptional && manifest != null && !manifest.isFile()) {throw new IllegalArgumentException(String.format("Main Manifest missing for variant %1$s. Expected path: %2$s",getVariantName(), getManifest().getAbsolutePath()));} }該任務(wù)是校驗(yàn)操作,就是校驗(yàn)清單文件的合法性,不合法則中斷任務(wù),拋出異常
配置階段操作
@Override public void execute(@NonNull CheckManifest checkManifestTask) {//1. 設(shè)置任務(wù)實(shí)現(xiàn)類scope.getTaskContainer().setCheckManifestTask(checkManifestTask);//2. 設(shè)置相關(guān)入?yún)?/span>checkManifestTask.setVariantName(scope.getVariantData().getVariantConfiguration().getFullName());checkManifestTask.setOptional(isManifestOptional);checkManifestTask.manifest =scope.getVariantData().getVariantConfiguration().getMainManifest();//3. 出參目錄配置checkManifestTask.fakeOutputDir =new File(scope.getGlobalScope().getIntermediatesDir(),"check-manifest/" + scope.getVariantConfiguration().getDirName()); }依賴任務(wù):preBuildTask
Task6 generateDebugBuildConfig
BuildConfig.java想必大家非常熟悉,該任務(wù)就是生成它
1. input/output
taskName:generateDebugBuildConfig ========================================================= output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/generated/source/buildConfig/debug ========================END==============================2. 核心類(GenerateBuildConfig)
@TaskAction void generate() throws IOException {//1. 清理操作 // must clear the folder in case the packagename changed, otherwise,// there'll be two classes.File destinationDir = getSourceOutputDir();FileUtils.cleanOutputDir(destinationDir);//2. 構(gòu)建一個(gè)BuildConfig生成器,用于生成BuildConfig.java BuildConfigGenerator generator = new BuildConfigGenerator(getSourceOutputDir(),getBuildConfigPackageName());// Hack (see IDEA-100046): We want to avoid reporting "condition is always true"// from the data flow inspection, so use a non-constant value. However, that defeats// the purpose of this flag (when not in debug mode, if (BuildConfig.DEBUG && ...) will// be completely removed by the compiler), so as a hack we do it only for the case// where debug is true, which is the most likely scenario while the user is looking// at source code.//map.put(PH_DEBUG, Boolean.toString(mDebug));//3. 給生成器對象配置6個(gè)固定字段// DEBUG、APPLICATION_ID、BUILD_TYPE、FLAVOR、VERSION_CODE、VERSION_NAME generator.addField("boolean","DEBUG",isDebuggable() ? "Boolean.parseBoolean(\"true\")" : "false").addField("String", "APPLICATION_ID", '"' + appPackageName.get() + '"').addField("String", "BUILD_TYPE", '"' + getBuildTypeName() + '"').addField("String", "FLAVOR", '"' + getFlavorName() + '"').addField("int", "VERSION_CODE", Integer.toString(getVersionCode())).addField("String", "VERSION_NAME", '"' + Strings.nullToEmpty(getVersionName()) + '"').addItems(getItems());//4. getItems,則是我們在build.gradle自定義的字段屬性值//5. 生成flavor字段List<String> flavors = getFlavorNamesWithDimensionNames();int count = flavors.size();if (count > 1) {for (int i = 0; i < count; i += 2) {generator.addField("String", "FLAVOR_" + flavors.get(i + 1), '"' + flavors.get(i) + '"');}}generator.generate();}在build.gralde -> defaultConfig 添加
buildConfigField("String", "name", "\"xiaobaoyihao\"")執(zhí)行
./gradlew generateDebugBuildConfig看下BuildConfig.java結(jié)構(gòu)
/*** Automatically generated file. DO NOT MODIFY*/ package com.gradle.task.demo;public final class BuildConfig {public static final boolean DEBUG = Boolean.parseBoolean("true");public static final String APPLICATION_ID = "com.gradle.task.demo";public static final String BUILD_TYPE = "debug";public static final String FLAVOR = "";public static final int VERSION_CODE = 1;public static final String VERSION_NAME = "1.0";// Fields from default config.public static final String name = "xiaobaoyihao"; }可以看到自定義屬性字段成功寫入到BuildConfig.java類中了;至于類生成具體實(shí)現(xiàn)細(xì)節(jié)是在generate方法中,主要使用了文件輸出流配合JavaWriter類操作來協(xié)調(diào)完成的,有興趣自行研究
//BuildConfigGenerator.java public void generate() throws IOException {File pkgFolder = getFolderPath();if (!pkgFolder.isDirectory()) {if (!pkgFolder.mkdirs()) {throw new RuntimeException("Failed to create " + pkgFolder.getAbsolutePath());}}File buildConfigJava = new File(pkgFolder, BUILD_CONFIG_NAME);Closer closer = Closer.create();try {FileOutputStream fos = closer.register(new FileOutputStream(buildConfigJava));OutputStreamWriter out = closer.register(new OutputStreamWriter(fos, Charsets.UTF_8));JavaWriter writer = closer.register(new JavaWriter(out));//1. 生成文件頭注釋說明文案writer.emitJavadoc("Automatically generated file. DO NOT MODIFY").emitPackage(mBuildConfigPackageName).beginType("BuildConfig", "class", PUBLIC_FINAL);//2. 就是上面提到的6個(gè)固定字段屬性for (ClassField field : mFields) {emitClassField(writer, field);}//3. 自定義屬性字段for (Object item : mItems) {if (item instanceof ClassField) {emitClassField(writer, (ClassField) item);} else if (item instanceof String) {writer.emitSingleLineComment((String) item);}}writer.endType();} catch (Throwable e) {throw closer.rethrow(e);} finally {closer.close();} }Task7 prepareLintJar
1. input/ouput
taskName:prepareLintJar ========================================================= output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/lint_jar/global/prepareLintJar/lint.jar2. 核心類(PrepareLintJar)
/*** Task that takes the configuration result, and check that it's correct.** <p>Then copies it in the build folder to (re)publish it. This is not super efficient but because* publishing is done at config time when we don't know yet what lint.jar file we're going to* publish, we have to do this.*/ public class PrepareLintJar extends DefaultTask {...@TaskActionpublic void prepare() throws IOException {// there could be more than one files if the dependency is on a sub-projects that// publishes its compile dependencies. Rather than query getSingleFile and fail with// a weird message, do a manual check//1. 獲取項(xiàng)目中依賴的自定義lint規(guī)則jar,此處gradle插件對其做了限制,只能有一個(gè)lint.jar超過一個(gè)會(huì)build失敗Set<File> files = lintChecks.getFiles();if (files.size() > 1) {throw new RuntimeException("Found more than one jar in the '"+ VariantDependencies.CONFIG_NAME_LINTCHECKS+ "' configuration. Only one file is supported. If using a separate Gradle project, make sure compilation dependencies are using compileOnly");}//2. 如果項(xiàng)目中沒有自定義lint規(guī)則,則清理文件,否則copyt lint.jar到指定目錄if (files.isEmpty()) {if (outputLintJar.isFile()) {FileUtils.delete(outputLintJar);}} else {FileUtils.mkdirs(outputLintJar.getParentFile());Files.copy(Iterables.getOnlyElement(files), outputLintJar);}}//ConfigAction.java@Overridepublic void execute(@NonNull PrepareLintJar task) { //指定輸出位置task.outputLintJar =scope.getArtifacts().appendArtifact(InternalArtifactType.LINT_JAR, task, FN_LINT_JAR);//讀取自定義規(guī)則文件集合 task.lintChecks = scope.getLocalCustomLintChecks();} }從上可以看出這個(gè)任務(wù)就是copy功能,lint.jar不能超過1個(gè),也就是說如果項(xiàng)目中存在混合語言時(shí),如果你要對其進(jìn)行各自自定義一套check規(guī)則(每套規(guī)則會(huì)生成一個(gè)lint.jar),那對不起,執(zhí)行到這任務(wù)直接中斷,這應(yīng)該算Android插件一個(gè)缺陷吧,高版本已修復(fù);
關(guān)于如何自定義check規(guī)則,可以參考如下官網(wǎng)相關(guān)鏈接
https://developer.android.com/studio/write/lint?hl=zh-cn
https://github.com/googlesamples/android-custom-lint-rules.git
http://tools.android.com/tips/lint-custom-rules
Task8 mainApkListPersistenceDebug
1. input/output
taskName:mainApkListPersistenceDebug ========================================================= output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/apk_list/debug/mainApkListPersistenceDebug/apk-list.gson格式化后的apk-list.gson
[{"type": "MAIN","splits": [],"versionCode": 1,"versionName": "1.0","enabled": true,"outputFile": "app-debug.apk","fullName": "debug","baseName": "debug"} ]這個(gè)任務(wù)其實(shí)就是生成一個(gè)apk信息的gson文件,看下核心入口代碼
2. 核心類(MainApkListPersistence)
這個(gè)類非常簡短,我直接全部貼出來吧,有意思的是它是kotlin寫的
/*** Task to persist the {@see OutputScope#apkdatas} which allows downstream tasks to depend* on the {@see InternalArtifactType#APK_LIST} rather than on various complicated data structures.* This also allow to record the choices made during configuration time about what APKs will be* produced and which ones are enabled.*/ open class MainApkListPersistence : AndroidVariantTask() {@get:OutputFilelateinit var outputFile: Fileprivate set@get:Inputlateinit var apkData : Collection<ApkData>private set@TaskActionfun fullTaskAction() {//1. 清理操作FileUtils.deleteIfExists(outputFile)//2. 返回一個(gè)apk信息的字符串val apkDataList = ExistingBuildElements.persistApkList(apkData)//3. 寫入文件FileUtils.createFile(outputFile, apkDataList)}class ConfigAction(val scope: VariantScope) :TaskConfigAction<MainApkListPersistence> {override fun getName() = scope.getTaskName("mainApkListPersistence")override fun getType() = MainApkListPersistence::class.javaoverride fun execute(task: MainApkListPersistence) {task.variantName = scope.fullVariantNametask.apkData = scope.outputScope.apkDatastask.outputFile = scope.artifacts.appendArtifact(InternalArtifactType.APK_LIST,task,SdkConstants.FN_APK_LIST)}} }可以看到關(guān)鍵代碼其實(shí)是在第二部操作中,我們看看內(nèi)部如何實(shí)現(xiàn)的,
//ExistingBuildElements.kt @JvmStatic fun persistApkList(apkInfos: Collection<ApkInfo>): String {val gsonBuilder = GsonBuilder()gsonBuilder.registerTypeHierarchyAdapter(ApkInfo::class.java, ApkInfoAdapter())val gson = gsonBuilder.create()return gson.toJson(apkInfos) }//ApkInfoAdapter.kt override fun write(out: JsonWriter, value: ApkInfo?) {if (value == null) {out.nullValue()return}out.beginObject()out.name("type").value(value.type.toString())out.name("splits").beginArray()for (filter in value.filters) {out.beginObject()out.name("filterType").value(filter.filterType)out.name("value").value(filter.identifier)out.endObject()}out.endArray()out.name("versionCode").value(value.versionCode.toLong())if (value.versionName != null) {out.name("versionName").value(value.versionName)}out.name("enabled").value(value.isEnabled)if (value.filterName != null) {out.name("filterName").value(value.filterName)}if (value.outputFileName != null) {out.name("outputFile").value(value.outputFileName)}out.name("fullName").value(value.fullName)out.name("baseName").value(value.baseName)out.endObject() }json字符串的構(gòu)造其實(shí)是依賴于goole自定義的ApkInfoAdapter適配器類來實(shí)現(xiàn)的,具體不說啦。
今天就到這吧。。。
👇
Android構(gòu)建流程——上篇
Android構(gòu)建流程——下篇
總結(jié)
以上是生活随笔為你收集整理的Android构建流程——篇三的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android构建流程——篇二
- 下一篇: Android构建流程——篇四