首先,如何查看gradle源码,我们在项目里依赖com.android.tools.build:gradle即可,如下:
compile gradleApi()
compile 'com.android.tools.build:gradle:2.3.3'
sync gradle后就可以看到相关的源码了
我们要了解的是apk的打包过程,实际上是gradle的一个插件application
apply plugin: 'com.android.application'
所以我们在gradle的源码下找到AppPligin,其部分源码如下:
public class AppPlugin extends BasePlugin implements Plugin<Project> {@Injectpublic AppPlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {super(instantiator, registry);}...@NonNull@Overrideprotected TaskManager createTaskManager(@NonNull Project project,@NonNull AndroidBuilder androidBuilder,@NonNull DataBindingBuilder dataBindingBuilder,@NonNull AndroidConfig androidConfig,@NonNull SdkHandler sdkHandler,@NonNull NdkHandler ndkHandler,@NonNull DependencyManager dependencyManager,@NonNull ToolingModelBuilderRegistry toolingRegistry,@NonNull Recorder recorder) {return new ApplicationTaskManager(project,androidBuilder,dataBindingBuilder,androidConfig,sdkHandler,ndkHandler,dependencyManager,toolingRegistry,recorder);}@Overridepublic void apply(@NonNull Project project) {super.apply(project);}...
}

这里我们关注createTaskManager函数,可以看到它返回了一个ApplicationTaskManager对象,这个类的部分源码如下:

public class ApplicationTaskManager extends TaskManager {...@Overridepublic void createTasksForVariantData(@NonNull final TaskFactory tasks,@NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {assert variantData instanceof ApplicationVariantData;final VariantScope variantScope = variantData.getScope();createAnchorTasks(tasks, variantScope);createCheckManifestTask(tasks, variantScope);handleMicroApp(tasks, variantScope);// Create all current streams (dependencies mostly at this point)createDependencyStreams(tasks, variantScope);// Add a task to process the manifest(s)recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createMergeAppManifestsTask(tasks, variantScope));// Add a task to create the res valuesrecorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createGenerateResValuesTask(tasks, variantScope));// Add a task to compile renderscript files.recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createRenderscriptTask(tasks, variantScope));// Add a task to merge the resource foldersrecorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK,project.getPath(),variantScope.getFullVariantName(),(Recorder.VoidBlock) () -> createMergeResourcesTask(tasks, variantScope));// Add a task to merge the asset foldersrecorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createMergeAssetsTask(tasks, variantScope));// Add a task to create the BuildConfig classrecorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createBuildConfigTask(tasks, variantScope));recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_PROCESS_RES_TASK,project.getPath(),variantScope.getFullVariantName(),() -> {// Add a task to process the Android Resources and generate source filescreateApkProcessResTask(tasks, variantScope);// Add a task to process the java resourcescreateProcessJavaResTasks(tasks, variantScope);});recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_AIDL_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createAidlTask(tasks, variantScope));recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_SHADER_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createShaderTask(tasks, variantScope));// Add NDK tasksif (!isComponentModelPlugin) {recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_NDK_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createNdkTasks(tasks, variantScope));} else {if (variantData.compileTask != null) {variantData.compileTask.dependsOn(getNdkBuildable(variantData));} else {variantScope.getCompileTask().dependsOn(tasks, getNdkBuildable(variantData));}}variantScope.setNdkBuildable(getNdkBuildable(variantData));// Add external native build tasksrecorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_EXTERNAL_NATIVE_BUILD_TASK,project.getPath(),variantScope.getFullVariantName(),() -> {createExternalNativeBuildJsonGenerators(variantScope);createExternalNativeBuildTasks(tasks, variantScope);});// Add a task to merge the jni libs foldersrecorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_JNILIBS_FOLDERS_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createMergeJniLibFoldersTasks(tasks, variantScope));// Add a compile taskrecorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_COMPILE_TASK,project.getPath(),variantScope.getFullVariantName(),() -> {CoreJackOptions jackOptions =variantData.getVariantConfiguration().getJackOptions();// create data binding merge task before the javac task so that it can// parse jars before any consumercreateDataBindingMergeArtifactsTaskIfNecessary(tasks, variantScope);AndroidTask<? extends JavaCompile> javacTask =createJavacTask(tasks, variantScope);if (jackOptions.isEnabled()) {AndroidTask<TransformTask> jackTask =createJackTask(tasks, variantScope, true /*compileJavaSource*/);setJavaCompilerTask(jackTask, tasks, variantScope);} else {// Prevent the use of java 1.8 without jack, which would otherwise cause an// internal javac error.if (variantScope.getGlobalScope().getExtension().getCompileOptions().getTargetCompatibility().isJava8Compatible()) {// Only warn for users of retrolambda and dexguardif (project.getPlugins().hasPlugin("me.tatarka.retrolambda")|| project.getPlugins().hasPlugin("dexguard")) {getLogger().warn("Jack is disabled, but one of the plugins you "+ "are using supports Java 8 language "+ "features.");} else {androidBuilder.getErrorReporter().handleSyncError(variantScope.getVariantConfiguration().getFullName(),SyncIssue.TYPE_JACK_REQUIRED_FOR_JAVA_8_LANGUAGE_FEATURES,"Jack is required to support java 8 language "+ "features. Either enable Jack or remove "+ "sourceCompatibility "+ "JavaVersion.VERSION_1_8.");}}addJavacClassesStream(variantScope);setJavaCompilerTask(javacTask, tasks, variantScope);getAndroidTasks().create(tasks,new AndroidJarTask.JarClassesConfigAction(variantScope));createPostCompilationTasks(tasks, variantScope);}});// Add data binding tasks if enabledcreateDataBindingTasksIfNecessary(tasks, variantScope);createStripNativeLibraryTask(tasks, variantScope);if (variantData.getSplitHandlingPolicy().equals(SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY)) {if (getExtension().getBuildToolsRevision().getMajor() < 21) {throw new RuntimeException("Pure splits can only be used with buildtools 21 and later");}recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_SPLIT_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createSplitTasks(tasks, variantScope));}recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_PACKAGING_TASK,project.getPath(),variantScope.getFullVariantName(),() -> {@NullableAndroidTask<BuildInfoWriterTask> fullBuildInfoGeneratorTask =createInstantRunPackagingTasks(tasks, variantScope);createPackagingTask(tasks, variantScope, true /*publishApk*/, fullBuildInfoGeneratorTask);});// create the lint tasks.recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_LINT_TASK,project.getPath(),variantScope.getFullVariantName(),() -> createLintTasks(tasks, variantScope));}...
}
在其createTasksForVariantData函数中,我们可以看到整个编译打包流程的所有task(不包括附加的),这里其实就概括了整个打包的流程,如下:
  • MERGE_MANIFEST
  • GENERATE_RES_VALUES
  • CREATE_RENDERSCRIPT
  • MERGE_RESOURCES
  • MERGE_ASSETS
  • BUILD_CONFIG
  • PROCESS_RES
  • AIDL
  • SHADER
  • NDK
  • EXTERNAL_NATIVE_BUILD
  • MERGE_JNILIBS_FOLDERS
  • COMPILE
  • SPLIT(这个是分包,只在21以上系统才会执行)
  • PACKAGING
  • LINT
基本上根据名字就知道在做什么,这里就不一个个细说了,我们重点关注PROCESS_RES这个task:
recorder.record(ExecutionType.APP_TASK_MANAGER_CREATE_PROCESS_RES_TASK,project.getPath(),variantScope.getFullVariantName(),() -> {// Add a task to process the Android Resources and generate source filescreateApkProcessResTask(tasks, variantScope);// Add a task to process the java resourcescreateProcessJavaResTasks(tasks, variantScope);});

它执行了两个小的task,我们来看createApkProcessResTask这个函数,它是ApplicationTaskManager的父类TaskManager的一个函数,代码如下:

public void createApkProcessResTask(@NonNull TaskFactory tasks,@NonNull VariantScope scope) {createProcessResTask(tasks,scope,new File(globalScope.getIntermediatesDir(),"symbols/" + scope.getVariantData().getVariantConfiguration().getDirName()),true);
}public void createProcessResTask(@NonNull TaskFactory tasks,@NonNull VariantScope scope,@Nullable File symbolLocation,boolean generateResourcePackage) {...// loop on all outputs. The only difference will be the name of the task, and location// of the generated data.for (BaseVariantOutputData vod : variantData.getOutputs()) {final VariantOutputScope variantOutputScope = vod.getScope();variantOutputScope.setProcessResourcesTask(androidTasks.create(tasks,new ProcessAndroidResources.ConfigAction(variantOutputScope, symbolLocation,generateResourcePackage,useAaptToGenerateLegacyMultidexMainDexProguardRules)));// always depend on merge res,variantOutputScope.getProcessResourcesTask().dependsOn(tasks,scope.getMergeResourcesTask());if (scope.getDataBindingProcessLayoutsTask() != null) {variantOutputScope.getProcessResourcesTask().dependsOn(tasks,scope.getDataBindingProcessLayoutsTask().getName());}variantOutputScope.getProcessResourcesTask().dependsOn(tasks, variantOutputScope.getManifestProcessorTask());if (vod.getMainOutputFile().getFilter(DENSITY) == null) {scope.setGenerateRClassTask(variantOutputScope.getProcessResourcesTask());scope.getSourceGenTask().optionalDependsOn(tasks,variantOutputScope.getProcessResourcesTask());}}
}

这里先是用到了ProcessAndroidResources的一个子类ConfigAction。
接着下面的代码则是规定了这个task的依赖规则,比如

variantOutputScope.getProcessResourcesTask().dependsOn(tasks,scope.getMergeResourcesTask());

必须依赖MergeResources,即MergeResources这个task执行后才能执行。

让我们回到ProcessAndroidResources,它的子类ConfigAction部分源码如下:

public static class ConfigAction implements TaskConfigAction<ProcessAndroidResources> {...@Overridepublic void execute(@NonNull ProcessAndroidResources processResources) {...if (variantOutputData.getMainOutputFile().getFilter(OutputFile.DENSITY) == null&& variantData.generateRClassTask == null) {...processResources.setSourceOutputDir(scope.getVariantScope().getRClassSourceOutputDir());processResources.setTextSymbolOutputDir(symbolLocation);if (config.getBuildType().isMinifyEnabled()) {if (config.getBuildType().isShrinkResources() && config.getJackOptions().isEnabled()) {LoggingUtil.displayWarning(Logging.getLogger(getClass()),scope.getGlobalScope().getProject(),"shrinkResources does not yet work with useJack=true");}processResources.setProguardOutputFile(scope.getVariantScope().getProcessAndroidResourcesProguardOutputFile());} ...}ConventionMappingHelper.map(processResources, "manifestFile", new Callable<File>() {@Overridepublic File call() throws Exception {return variantOutputData.manifestProcessorTask.getOutputFile();}});...}
}

在它的execute函数中可以看到设置了一些信息,比如各种文件的输出路径,这里我们拿SourceOutputDir来举例:

processResources.setSourceOutputDir(scope.getVariantScope().getRClassSourceOutputDir());

getRClassSourceOutputDir函数是抽象类VariantScope的一个抽象方法,它的实现是在VariantScopeImpl中,代码如下:

@Override
@NonNull
public File getRClassSourceOutputDir() {return new File(globalScope.getGeneratedDir(),"source/r/" + getVariantConfiguration().getDirName());
}

其中

  • globalScope.getGeneratedDir()就是[project]/app/build/generated/目录
  • getVariantConfiguration().getDirName()得到的是BuildType及Flavors(如果有),如debug/或baidu/debug/
  • getRClassSourceOutputDir函数得到的路径就是“[project]/app/build/generated/source/r/debug/”
关注过build/目录的同学应该知道,“[project]/app/build/generated/source/r/debug/”下在相应的包名目录下是R.java文件
那么这个路径在哪里使用,如何生成R.java的?
回到execute函数中,processResources实际上就是ProcessAndroidResources的一个对象,既然有setSourceOutputDir函数,那么也有个对应的get函数。
这个get函数则在ProcessAndroidResources的doFullTaskAction函数中被调用,这个函数部分源码如下:
protected void doFullTaskAction() throws IOException {// we have to clean the source folder output in case the package name changed.File srcOut = getSourceOutputDir();...try {...AaptPackageConfig.Builder config =new AaptPackageConfig.Builder().setManifestFile(manifestFileToPackage).setOptions(getAaptOptions()).setResourceDir(getResDir()).setLibraries(getAndroidDependencies()).setCustomPackageForR(getPackageForR()).setSymbolOutputDir(getTextSymbolOutputDir()).setSourceOutputDir(srcOut).setResourceOutputApk(resOutBaseNameFile).setProguardOutputFile(getProguardOutputFile()).setMainDexListProguardOutputFile(getMainDexListProguardOutputFile()).setVariantType(getType()).setDebuggable(getDebuggable()).setPseudoLocalize(getPseudoLocalesEnabled()).setResourceConfigs(getResourceConfigs()).setSplits(getSplits()).setPreferredDensity(preferredDensity).setBaseFeature(getBaseFeature()).setPreviousFeatures(getPreviousFeatures());builder.processResources(aapt, config, getEnforceUniquePackageName());...} catch (IOException | InterruptedException | ProcessException e) {throw new RuntimeException(e);}
}

将这些信息又封装到一个AaptPackageConfig.Builder对象中,最后调用了一个processResources函数。
这个processResources是AndroidBuilder的一个函数,部分源码如下:

public void processResources(@NonNull Aapt aapt,@NonNull AaptPackageConfig.Builder aaptConfigBuilder,boolean enforceUniquePackageName)throws IOException, InterruptedException, ProcessException {checkState(mTargetInfo != null,"Cannot call processResources() before setTargetInfo() is called.");aaptConfigBuilder.setBuildToolInfo(mTargetInfo.getBuildTools());aaptConfigBuilder.setAndroidTarget(mTargetInfo.getTarget());aaptConfigBuilder.setLogger(mLogger);AaptPackageConfig aaptConfig = aaptConfigBuilder.build();try {aapt.link(aaptConfig).get();} catch (Exception e) {throw new ProcessException("Failed to execute aapt", e);}...
}

执行了aapt.link(),aapt是一个Aapt对象,Aapt是一个抽象类,link方法是在AbstractAapt中实现的,源码如下:

public ListenableFuture<Void> link(@NonNull AaptPackageConfig config)throws AaptException {validatePackageConfig(config);return makeValidatedPackage(config);
}

首先调用validatePackageConfig函数检查参数是否正确,然后执行了makeValidatedPackage函数。
在AbstractAapt中makeValidatedPackage是抽象方法,它的实现在AbstractProcessExecutionAapt类中,源码如下:

protected ListenableFuture<Void> makeValidatedPackage(@NonNull AaptPackageConfig config)throws AaptException {ProcessInfoBuilder builder = makePackageProcessBuilder(config);final ProcessInfo processInfo = builder.createProcess();ListenableFuture<ProcessResult> execResult = mProcessExecutor.submit(processInfo,mProcessOutputHandler);final SettableFuture<Void> result = SettableFuture.create();Futures.addCallback(execResult, new FutureCallback<ProcessResult>() {...});return result;
}

创建了一个ProcessInfoBuilder对象,然后执行并得到结果,那么重点就是这个ProcessInfoBuilder对象里,来看看makePackageProcessBuilder这个函数。
同样这个函数也是抽象函数,有两个类对它进行了实现AaptV1和OutOfProcessAaptV2,很明显这与当前android sdk下的aapt版本有关。
两个方法大致类似,我们只看AaptV1的,源码如下:

protected ProcessInfoBuilder makePackageProcessBuilder(@NonNull AaptPackageConfig config)throws AaptException {ProcessInfoBuilder builder = new ProcessInfoBuilder();...// outputsif (config.getSourceOutputDir() != null) {builder.addArgs("-m");builder.addArgs("-J", FileUtils.toExportableSystemDependentPath(config.getSourceOutputDir()));}if (config.getResourceOutputApk() != null) {builder.addArgs("-F", config.getResourceOutputApk().getAbsolutePath());}...// Add the feature-split configuration if needed.if (config.getBaseFeature() != null) {builder.addArgs("--feature-of", config.getBaseFeature().getAbsolutePath());// --feature-after requires --feature-of to be set so these are only parsed if base// feature was set.for (File previousFeature : config.getPreviousFeatures()) {builder.addArgs("--feature-after", previousFeature.getAbsolutePath());}}return builder;
}

可以看到这个函数实际上是组合了一条aapt命令,添加了各种参数,其中我们关注的getSourceOutputDir则是“-J”这个参数的值。

查看aapt的说明:

Modifiers:-a  print Android-specific data (resources, manifest) when listing-c  specify which configurations to include.  The default is allconfigurations.  The value of the parameter should be a commaseparated list of configuration values.  Locales should be specifiedas either a language or language-region pair.  Some examples:enport,enport,land,en_US-d  one or more device assets to include, separated by commas-f  force overwrite of existing files-g  specify a pixel tolerance to force images to grayscale, default 0-j  specify a jar or zip file containing classes to include-k  junk path of file(s) added-m  make package directories under location specified by -J-u  update existing packages (add new, replace older, remove deleted files)-v  verbose output-x  create extending (non-application) resource IDs-z  require localization of resource attributes marked withlocalization="suggested"-A  additional directory in which to find raw asset files-G  A file to output proguard options into.-D  A file to output proguard options for the main dex into.-F  specify the apk file to output-I  add an existing package to base include set-J  specify where to output R.java resource constant definitions-M  specify full path to AndroidManifest.xml to include in zip-P  specify where to output public resource definitions-S  directory in which to find resources.  Multiple directories will be scanned

可以看到-J这个参数的含义是设置R.java文件的输出路径,这样我们就找到了源头。

在看其他代码,可以发现同样是为aapt命令添加一些运行参数,比如asrc文件的输出路径等
然后回到之前,执行这条命令,就完成了这个task。

总结一下,在processResources这个过程中实际上是执行了一个aapt命令对资源文件进行编译,同时生成R文件等一些相关文件。

gradle编译打包过程 之 ProcessAndroidResources的源码分析相关推荐

  1. hadoop作业初始化过程详解(源码分析第三篇)

    (一)概述 我们在上一篇blog已经详细的分析了一个作业从用户输入提交命令到到达JobTracker之前的各个过程.在作业到达JobTracker之后初始化之前,JobTracker会通过submit ...

  2. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )

    文章目录 前言 一.ActivityManagerService.attachApplicationLocked 二.ActivityStackSupervisor.attachApplication ...

  3. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 二 )

    文章目录 前言 一.ActivityThread 类 handleLaunchActivity -> performLaunchActivity 方法 二.Instrumentation.new ...

  4. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 一 )

    文章目录 前言 一.ClientTransactionHandler.scheduleTransaction 二.ActivityThread.H 处理 EXECUTE_TRANSACTION 消息 ...

  5. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )

    文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...

  6. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 )

    文章目录 一.Activity 启动源码分析 ( AMS | ActivityManagerService ) 1.Instrumentation 调用 AMS 方法 2.ActivityStarte ...

  7. 【Android 启动过程】Activity 启动源码分析 ( Activity -> AMS、主线程阶段 )

    文章目录 一.Activity 启动源码分析 ( Activity -> AMS 阶段 ) 一.Activity 启动源码分析 ( Activity -> AMS 阶段 ) 调用 star ...

  8. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 一 )

    文章目录 一.ActivityThread 主函数启动 二.ActivityThread 绑定 ApplicationThread 三.AMS attachApplication -> atta ...

  9. eos交易同步过程和区块生产过程源码分析

    交易同步过程 1 通过命令cleos调用 cleos transfer ${from_account} ${to_account} ${quantity} 发起交易 2 eos调用chain_plug ...

最新文章

  1. 敏捷软件开发(c#版)文摘
  2. 浅谈k8s cni 插件
  3. idea 执行java maven,IDEA的run maven方式启动步骤详解
  4. 百练OJ:1007:DNA排序
  5. vue工程全局设置ajax的等待动效
  6. xilinx芯片管脚使用限制_修复焊接BGA芯片过程
  7. 怎样更改SQL Server 2008的身份验证方式
  8. ORACLE 锁表处理,解锁释放session
  9. boost::asio 网络传输错误码的一些实验结果(recv error_code)
  10. 计算机字体库被删了,win7系统下word字体库不想要的字体如何删除
  11. 七、决策树算法和集成算法
  12. win10文件夹加密_文件隐私保护工具文件夹隐藏精灵
  13. jsp值choose标签
  14. odd ratio置信区间的计算,你学会了吗?
  15. gson的解析demo JAVA
  16. android entries属性,ListPreference需要设置两个属性:android:entries和android:entryValues...
  17. SQL 高级教程:SQL BETWEEN 操作符
  18. python使用Axes3D画三维图加入legend图例时报错AttributeError: ‘Poly3DCollection‘ object has no attribute ‘_edgecolo
  19. 大数据有哪些软件可以使用?
  20. STM32 电机驱动

热门文章

  1. iTunes备份注意
  2. 让ie6(opera)支持微软雅黑字体
  3. 对比kCCPositionTypeFree和kCCPositionTypeRelative两种粒子移动类型
  4. android Bimtap 各种图片处理方法、图片特效
  5. AutoHotkey纯命令获取Chrome等浏览器的当前网址
  6. spreedrest
  7. 6.Django与Ajax
  8. 001 Cisco router prewired
  9. STM8启动分析及IAP
  10. ViewController类中得方法和属性的用途