日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

gradle编译打包过程 之 ProcessAndroidResources的源码分析

發布時間:2024/4/15 Android 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gradle编译打包过程 之 ProcessAndroidResources的源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
首先,如何查看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的源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。