引入源码
首先,如何查看gradle源码,我们在项目里依赖com.android.tools.build:gradle即可,如下:
代码语言:javascript复制compile gradleApi()
compile 'com.android.tools.build:gradle:2.3.3'
sync gradle后就可以看到相关的源码了
分析
我们要了解的是apk的打包过程,实际上是gradle的一个插件application
代码语言:javascript复制apply plugin: 'com.android.application'
所以我们在gradle的源码下找到AppPligin,其部分源码如下:
代码语言:javascript复制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对象,这个类的部分源码如下:
代码语言:javascript复制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:
代码语言:javascript复制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的一个函数,代码如下:
代码语言:javascript复制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的依赖规则,比如
代码语言:javascript复制variantOutputScope.getProcessResourcesTask().dependsOn(tasks,
scope.getMergeResourcesTask());
必须依赖MergeResources,即MergeResources这个task执行后才能执行。
让我们回到ProcessAndroidResources,它的子类ConfigAction部分源码如下:
代码语言:javascript复制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来举例:
代码语言:javascript复制processResources.setSourceOutputDir(scope.getVariantScope().getRClassSourceOutputDir());
getRClassSourceOutputDir函数是抽象类VariantScope的一个抽象方法,它的实现是在VariantScopeImpl中,代码如下:
代码语言:javascript复制@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函数中被调用,这个函数部分源码如下:
代码语言:javascript复制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的一个函数,部分源码如下:
代码语言:javascript复制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中实现的,源码如下:
代码语言:javascript复制public ListenableFuture<Void> link(@NonNull AaptPackageConfig config)
throws AaptException {
validatePackageConfig(config);
return makeValidatedPackage(config);
}
首先调用validatePackageConfig函数检查参数是否正确,然后执行了makeValidatedPackage函数。
在AbstractAapt中makeValidatedPackage是抽象方法,它的实现在AbstractProcessExecutionAapt类中,源码如下:
代码语言:javascript复制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的,源码如下:
代码语言:javascript复制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的说明:
代码语言:javascript复制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文件等一些相关文件。