《利器!Android开发工具之谈》专栏系列:https://blog.csdn.net/column/details/16703.html
首先,如何查看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> { @Inject public AppPlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) { super(instantiator, registry); } ... @NonNull @Override protected 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); } @Override public void apply(@NonNull Project project) { super.apply(project); } ... }这里我们关注createTaskManager函数,可以看到它返回了一个ApplicationTaskManager对象,这个类的部分源码如下:
public class ApplicationTaskManager extends TaskManager { ... @Override public 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 values recorder.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 folders recorder.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 folders recorder.record( ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK, project.getPath(), variantScope.getFullVariantName(), () -> createMergeAssetsTask(tasks, variantScope)); // Add a task to create the BuildConfig class recorder.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 files createApkProcessResTask(tasks, variantScope); // Add a task to process the java resources createProcessJavaResTasks(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 tasks if (!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 tasks recorder.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 folders recorder.record( ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_JNILIBS_FOLDERS_TASK, project.getPath(), variantScope.getFullVariantName(), () -> createMergeJniLibFoldersTasks(tasks, variantScope)); // Add a compile task recorder.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 consumer createDataBindingMergeArtifactsTaskIfNecessary(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 dexguard if (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 enabled createDataBindingTasksIfNecessary(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(), () -> { @Nullable AndroidTask<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 files createApkProcessResTask(tasks, variantScope); // Add a task to process the java resources createProcessJavaResTasks(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> { ... @Override public 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>() { @Override public 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函数中被调用,这个函数部分源码如下:
那么这个路径在哪里使用,如何生成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(); ... // outputs if (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 all configurations. The value of the parameter should be a comma separated list of configuration values. Locales should be specified as either a language or language-region pair. Some examples: en port,en port,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 with localization="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文件等一些相关文件。
然后回到之前,执行这条命令,就完成了这个task。
总结一下,在processResources这个过程中实际上是执行了一个aapt命令对资源文件进行编译,同时生成R文件等一些相关文件。
《利器!Android开发工具之谈》专栏系列:https://blog.csdn.net/column/details/16703.html