自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入理解 Android Studio Sync 流程

精選
移動開發(fā) Android
本文首先介紹了 Android Studio Sync 流程中各個角色的作用及聯(lián)系,對 Sync 有一個較清晰的整體認(rèn)識,然后從源碼角度深入分析從觸發(fā) Sync 入口到 Gradle 構(gòu)建結(jié)束的階段,并詳細(xì)解釋了 Gradle Model、BuildAction 等關(guān)鍵概念

1. 初識 Sync

我們一般會把 Sync 理解為 Android Studio 的準(zhǔn)備階段,包括解析工程配置信息、下載遠(yuǎn)程依賴到本地、更新代碼索引等準(zhǔn)備工作,當(dāng)修改 gradle build 文件后,需要重新 Sync 將 Gradle 構(gòu)建配置信息同步到 IDE,進(jìn)而使 IDE 的功能及時應(yīng)用新的構(gòu)建配置,這些功能包括項目的 Gradle Task 列表展示、依賴信息展示等等。Sync 是 Android Studio 中獨有的概念,當(dāng)通過 Gradle 命令行程序構(gòu)建 Android 應(yīng)用時,只會經(jīng)歷 Gradle 定義的 Initialization、Configuration 和 Execution 生命周期,根本沒有 Sync 的概念。Android Studio 的 Sync 階段涉及到 IDE、Gradle、Plugin 等多個角色,梳理清楚這些角色各自的作用和聯(lián)系,才能清晰理解 Sync 的整體架構(gòu),下面分別來介紹這些角色:

圖片

IDE 層面:Android Studio 基于 IntelliJ IDEA 擴(kuò)展而來,復(fù)用了 IntelliJ IDEA 強(qiáng)大的代碼編輯器和開發(fā)者工具等 IDE 基礎(chǔ)能力,在此之上 Android Studio 提供了更多提高 Android 構(gòu)建效率的功能,如 Android 模擬器、代碼模版等等。另外 Android Studio 也將自身專業(yè)的 Android 應(yīng)用開發(fā)能力反哺給 IntelliJ IDEA,以 Android IDEA Plugin 的形式,使 IntelliJ IDEA 支持 Android 應(yīng)用開發(fā),二者互相賦能,相輔相成。

Gradle 層面:Gradle 是一個靈活而強(qiáng)大的開源構(gòu)建系統(tǒng),它除了提供跨平臺的可執(zhí)行程序支持命令行執(zhí)行 Gradle 構(gòu)建外,還專門提供了 Gradle Tooling API 編程 SDK,供外部更方便、更緊密的將 Gradle 構(gòu)建能力嵌入到 IDE 中,IntelliJ IDEA、Eclipse、VSCode 等 IDE 都采用了這種方式。在 Gradle 源碼中也有專門服務(wù)于 IntelliJ IDEA、Eclipse 等 IDE 的代碼模塊,構(gòu)建工具和 IDE 兩個角色之間同樣是互相賦能,強(qiáng)強(qiáng)聯(lián)合。

Plugin 層面:Plugin 層面包括 Android IDEA Plugin 和 Android Gradle Plugin,Android IDEA Plugin 為 IntelliJ IDEA/Android Studio 拓展了 Android 應(yīng)用開發(fā)能力;Android Gradle Plugin 為 Gradle 拓展了 Android 應(yīng)用構(gòu)建能力。谷歌通過這兩個 Plugin 將現(xiàn)代成熟優(yōu)秀的 IDE 開發(fā)能力和構(gòu)建工具聯(lián)合在一起為 Android 所用,相比于早期 Eclipse 加 ANT 構(gòu)建的開發(fā)方式,大幅提升了 Android 應(yīng)用開發(fā)效率和體驗。

2. Sync 流程分析

了解了 Sync 階段涉及到的角色以及它們之間的關(guān)系后,接下來深入 Android Studio 源碼,從代碼層面梳理清楚 Sync 的關(guān)鍵流程。

2.1 Android Studio 源碼分析

2.1.1 功能入口及準(zhǔn)備

在 Android Studio 中觸發(fā) Sync 操作后,會從最上層的入口類 GradleSyncInvoker 調(diào)用到實際負(fù)責(zé)解析 Gradle 構(gòu)建信息的 GradleProjectResolver,調(diào)用鏈如下圖所示:

圖片

調(diào)用過程中涉及到的關(guān)鍵類:

GradleSyncInvoker:觸發(fā) Sync 的入口類,在 Android Studio 多處需要執(zhí)行 Sync 的地方,都是通過調(diào)用此類的 requestProjectSync 方法來觸發(fā)的

ExternalSystemUtil:GradleSyncInvoker、GradleSyncExecutor 等類是專門針對 Sync 功能的封裝,Sync 是 Android Studio 中獨有的操作,在 IntelliJ IDEA 中并沒有 Sync 的概念,IntelliJ IDEA 通過 [Reload All Gradle Projects] 操作來觸發(fā)解析工程的 Gradle 構(gòu)建信息,直接從 ExternalSystemUtil 類開始執(zhí)行

GradleProjectResolver:負(fù)責(zé)具體執(zhí)行 Sync,其中 resolveProjectInfo 方法是具體執(zhí)行 Sync 邏輯的地方,該方法的定義如下:

public DataNode<ProjectData> resolveProjectInfo(
@NotNull ExternalSystemTaskId id,
@NotNull String projectPath,
boolean isPreviewMode,
@Nullable S settings,
@NotNull ExternalSystemTaskNotificationListener listener)
throws ExternalSystemException, IllegalArgumentException, IllegalStateException
  • id:本次 Sync 操作的唯一標(biāo)識,后續(xù)可通過調(diào)用 GradleProjectResolver 中的 cancelTask 方法取消本次 Sync 任務(wù)
  • projectPath:工程絕對路徑
  • settings:Sync 工程的配置參數(shù),可設(shè)置 Java 版本等
  • listener:用于監(jiān)聽此次 Sync 的過程及結(jié)果
  • isPreviewMode:是否要啟用預(yù)覽模式,Android Studio 首次打開未知來源項目時,會讓開發(fā)者選擇項目的打開方式,如下圖,若選擇 [Stay in Safe Mode] 則會以“預(yù)覽模式”運行項目,表示 IDE 僅可以瀏覽項目的源代碼,不會執(zhí)行或解析任何構(gòu)建任務(wù)和腳本

圖片

進(jìn)入 GradleProjectResolver 的 resolveProjectInfo 方法中后,首先會對預(yù)覽模式進(jìn)行處理,如下代碼所示,如果是預(yù)覽模式,則會簡單構(gòu)造出對應(yīng)的工程數(shù)據(jù)結(jié)構(gòu)后立馬返回,不進(jìn)行任何的解析行為:

if (isPreviewMode) {
String projectName = new File(projectPath).getName();
ProjectData projectData = new ProjectData(GradleConstants.SYSTEM_ID, projectName, projectPath, projectPath);
DataNode<ProjectData> projectDataNode = new DataNode<>(ProjectKeys.PROJECT, projectData, null);
......
return projectDataNode;
}

使用 Android Studio/IntelliJ IDEA 打開工程時,除了指定工程所在根目錄,還可以指定 Gradle 配置文件,這個邏輯在源碼中也有所體現(xiàn):

if (projectPathFile.isFile() && projectPath.endsWith(GradleConstants.EXTENSION) && projectPathFile.getParent() != null) {
projectDir = projectPathFile.getParent();
if (settings != null) {
List<String> arguments = settings.getArguments();
if (!arguments.contains("-b") && !arguments.contains("--build-file")) {
settings.withArguments("-b", projectPath);
}
}
} else {
projectDir = projectPath;
}

可以看到如果打開的不是工程根目錄而是配置文件,那就會把該配置文件所在的目錄作為工程路徑,并且會通過 --build-file 參數(shù)來指定配置文件,后續(xù)會傳入到 Gradle 中。對工程初步處理后,接著通過 Gradle Tooling API 獲取工程的 BuildEnvironment:

ModelBuilder<BuildEnvironment> modelBuilder = connection.model(BuildEnvironment.class);

調(diào)用 Gradle Tooling API 時其內(nèi)部會自動下載項目配置版本的 Gradle,也就是說,上面代碼中獲取 BuildEnvironment Model 的調(diào)用本身確保了 Gradle 的下載,即將 /grade/wrapper/gradle-wrapper.properties 里 distributionUrl 指定的 Gradle 下載到 GRADLE_HOME/wrapper/dists 下。

BuildEnvironment 是 Gradle 提供的 Gradle Model,Gradle Model 是一個非常重要的概念,當(dāng) IDE 通過 Gradle Tooling API 和 Gradle 交互時,傳輸?shù)木褪歉鞣N各樣的 Model,比如 Gradle 自帶的 GradleProject 、 BuildEnvironment 等,另外也可以在 Gradle Plugin 中通過 ToolingModelBuilderRegistry 注冊自定義的 Model,如 Android Gradle Plugin 中注冊了 AndroidProject Model,那就可以通過 Gradle Tooling API 獲取 Android 項目特有的 AndroidProject Model,從而獲取由 Android Gradle Plugin 提供的 Android 應(yīng)用相關(guān)工程信息。

2.1.2 配置 BuildAction

繼續(xù)分析 Android Studio Sync 源碼,接下來構(gòu)造了一個 ProjectImportAction,它實現(xiàn)了 Gradle Tooling API 中的 BuildAction 接口,BuildAction 定義如下:

public interface BuildAction<T> extends Serializable {

T execute(BuildController controller);
}

BuildAction 是即將傳入到 Gradle 構(gòu)建進(jìn)程中執(zhí)行的行為,并且可將結(jié)果數(shù)據(jù)序列化返回給調(diào)用方。這個 BuildAction至關(guān)重要,它是實際和 Gradle 通信的地方,其中實現(xiàn)了組織生成工程信息、下載依賴等功能,是 Sync 流程中的核心邏輯。BuildAction 再配合 Gradle Tooling API 中的 BuildActionExecuter,就可以將 BuildAction 交由 Gradle 觸發(fā)執(zhí)行了,在執(zhí)行之前,需先通過 BuildActionExecuter 配置 JVM 參數(shù)、Gradle 命令行參數(shù)以及環(huán)境變量等構(gòu)建信息:

private static void configureExecutionArgumentsAndVmOptions(@NotNull GradleExecutionSettings executionSettings,
@NotNull DefaultProjectResolverContext resolverCtx,
boolean isBuildSrcProject) {
executionSettings.withArgument("-Didea.sync.active=true");
if (resolverCtx.isResolveModulePerSourceSet()) {
executionSettings.withArgument("-Didea.resolveSourceSetDependencies=true");
}
if (!isBuildSrcProject) {
for (GradleBuildParticipant buildParticipant : executionSettings.getExecutionWorkspace().getBuildParticipants()) {
executionSettings.withArguments(GradleConstants.INCLUDE_BUILD_CMD_OPTION, buildParticipant.getProjectPath());
}
}
GradleImportCustomizer importCustomizer = GradleImportCustomizer.get();
GradleProjectResolverUtil.createProjectResolvers(resolverCtx).forEachOrdered(extension -> {
if (importCustomizer == null || importCustomizer.useExtraJvmArgs()) {

ParametersList parametersList = new ParametersList();
for (Pair<String, String> jvmArg : extension.getExtraJvmArgs()) {
parametersList.addProperty(jvmArg.first, jvmArg.second);
}
executionSettings.withVmOptions(parametersList.getParameters());
}

executionSettings.withArguments(extension.getExtraCommandLineArgs());
});
}

上述代碼較多,我們先只關(guān)注于 GradleExecutionSettings 的 withArgument 和 withVmOptions 方法,它們分別負(fù)責(zé)收集 Gradle 命令行參數(shù)和 JVM 參數(shù)。從代碼中可以看出,這些 Gradle 命令行參數(shù)及 JVM 參數(shù)大多數(shù)是從 extension 中收集而來,這里的 extension 是指 IntelliJ IDEA Plugin 中的擴(kuò)展,和擴(kuò)展點結(jié)合使用,可擴(kuò)展 IntelliJ IDEA 平臺特性或其他 IntelliJ IDEA Plugin 的功能特性,舉個例子,Gradle IDEA Plugin 提供了工程解析的擴(kuò)展點:

<extensionPoint 
qualifiedName="org.jetbrains.plugins.gradle.projectResolve"
interface="org.jetbrains.plugins.gradle.service.project.GradleProjectResolverExtension"/>

Android IDEA Plugin 中實現(xiàn)了此擴(kuò)展點,提供了 Android 工程解析的拓展:

<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<projectResolve implementation=
"com.android.tools.idea.gradle.project.sync.idea.AndroidGradleProjectResolver"
order="first"/>
......
</extensions>

接著來看 BuildAction 的參數(shù)配置邏輯,最終設(shè)置 JVM 參數(shù)的地方在 GradleExecutionHelper 的 prepare 方法中:

List<String> jvmArgs = settings.getJvmArguments();
BuildEnvironment buildEnvironment = getBuildEnvironment(connection, id, listener, (CancellationToken)null, settings);
if (!jvmArgs.isEmpty()) {

Collection<String> merged;
if (buildEnvironment != null) {


BuildIdentifier buildIdentifier = getBuildIdentifier(buildEnvironment);
List<String> buildJvmArguments = buildIdentifier == null || "buildSrc".equals(buildIdentifier.getRootDir().getName())
? ContainerUtil.emptyList()
: buildEnvironment.getJava().getJvmArguments();
merged = mergeBuildJvmArguments(buildJvmArguments, jvmArgs);
} else {
merged = jvmArgs;
}
List<String> filteredArgs = ContainerUtil.mapNotNull(merged, s -> StringUtil.isEmpty(s) ? null : s);
operation.setJvmArguments(ArrayUtilRt.toStringArray(filteredArgs));
}

如上代碼所示,JVM 參數(shù)的配置邏輯很簡單:將之前從一系列 GradleProjectResolve 擴(kuò)展中收集的、存放在 GradleExecutionSettings 中的 JVM 參數(shù)和 BuildEnvironment 中的 JVM 參數(shù)合并,然后調(diào)用 BuildActionExecuter 的 setJvmArguments 方法,將 JVM 參數(shù)設(shè)置給 BuildAction。Gradle 命令行參數(shù)同樣是在 GradleExecutionHelper 的 prepare 方法中配置:

...
List<String> filteredArgs = new ArrayList<>();
if (!settings.getArguments().isEmpty()) {
String loggableArgs = StringUtil.join(obfuscatePasswordParameters(settings.getArguments()), " ");
LOG.info("Passing command-line args to Gradle Tooling API: " + loggableArgs);

filteredArgs.addAll(ContainerUtil.mapNotNull(settings.getArguments(), s -> StringUtil.isEmpty(s) ? null : s));
...
}
filteredArgs.add("-Didea.active=true");
filteredArgs.add("-Didea.version=" + getIdeaVersion());
operation.withArguments(ArrayUtilRt.toStringArray(filteredArgs));

對于一個最簡單的 Kotlin App Demo 工程,Gradle 命令行參數(shù)如下:

來源

參數(shù)

Android Studio 源碼

--init-script
/private/var/folders/_4/j3fdr4nd0x7cf17yvt20f5c00000gp/T/ijmapper.gradle
-Didea.sync.active=true
-Didea.resolveSourceSetDependencies=true
-Porg.gradle.kotlin.dsl.provider.cid=676307056703202
-Pkotlin.mpp.enableIntransitiveMetadataConfiguration=true
--init-script/private/var/folders/_4/j3fdr4nd0x7cf17yvt20f5c00000gp/T/ijinit3.gradle
-Didea.active=true
-Didea.version=2021.3

Android IDEA Plugin 擴(kuò)展:com.android.tools.idea.gradle.project.sync.idea.AndroidGradleProjectResolver

--init-script
/private/var/folders/_4/j3fdr4nd0x7cf17yvt20f5c00000gp/T/sync.studio.tooling4770.gradle
-Djava.awt.headless=true
--stacktrace-Pandroid.injected.build.model.only=true
-Pandroid.injected.build.model.only.advanced=true
-Pandroid.injected.invoked.from.ide=true
-Pandroid.injected.build.model.only.versioned=3
-Pandroid.injected.studio.version=10.4.2
-Pandroid.injected.build.model.disable.src.download=true
-Pidea.gradle.do.not.build.tasks=true

Kotlin IDEA Plugin 擴(kuò)展:org.jetbrains.kotlin.idea.gradleJava.scripting.importing.KotlinDslScriptModelResolver

-Dorg.gradle.kotlin.dsl.provider.mode=classpath

Kotlin IDEA Plugin 擴(kuò)展:org.jetbrains.kotlin.idea.gradleJava.scripting.importing.KotlinDslScriptModelResolver

-Porg.gradle.kotlin.dsl.provider.cid=676307056703202

Kotlin IDEA Plugin 擴(kuò)展:org.jetbrains.kotlin.idea.gradleJava.configuration.KotlinMPPGradleProjectResolver

-Pkotlin.mpp.enableIntransitiveMetadataConfiguration=true

重點關(guān)注其中的 --init-script 命令行參數(shù),它可以指定一個初始化腳本,初始化腳本會在項目構(gòu)建腳本之前執(zhí)行。初始化腳本是 Gradle 提供的一個非常靈活的機(jī)制,除了命令行配置,還可以將初始化腳本命名為 init.gradle 放置到 USER_HOME/.gradle/ 下進(jìn)行配置。初始化腳本允許自定義所有項目的構(gòu)建邏輯,比如定義特定機(jī)器上所有項目的 JDK 路徑等環(huán)境信息。在上述 Kotlin App Demo 工程中,Android IDEA Plugin 擴(kuò)展配置了一個初始化腳本,內(nèi)容如下:

initscript {
dependencies {
classpath files([mapPath('/Users/bytedance/IDE/intellij-community/out/production/intellij.android.gradle-tooling'), mapPath('/Users/bytedance/IDE/intellij-community/out/production/intellij.android.gradle-tooling.impl'), mapPath('/Users/bytedance/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib/1.5.10-release-945/kotlin-stdlib-1.5.10-release-945.jar')])
}
}
allprojects {
apply plugin: com.android.ide.gradle.model.builder.AndroidStudioToolingPlugin
}

初始化腳本中對所有項目應(yīng)用了 AndroidStudioToolingPlugin 插件,此插件中通過 ToolingModelBuilderRegistry 注冊了 AdditionalClassifierArtifactsModel,這個 Gradle Model 中實現(xiàn)了下載依賴 sources 和 javadoc 的功能:

class AdditionalClassifierArtifactsModelBuilder : ParameterizedToolingModelBuilder<AdditionalClassifierArtifactsModelParameter> {
...

也就是說,Android IDEA Plugin 提供了依賴 sources 和 javadoc 的下載功能,當(dāng)通過 Gradle Tooling API 獲取 AdditionalClassifierArtifactsModel Gradle Model 的時候,會觸發(fā)依賴的 sources 和 javadoc 下載。

分析完 BuildAction 的 JVM 參數(shù)和 Gradle 命令行參數(shù)配置流程后,最后來看 BuildAction 環(huán)境變量的配置,最終配置的地方在 GradleExecutionHelper 的 setupEnvironment 方法中:

GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.withEnvironment(settings.getEnv());
commandLine.withParentEnvironmentType(
settings.isPassParentEnvs() ? GeneralCommandLine.ParentEnvironmentType.CONSOLE : GeneralCommandLine.ParentEnvironmentType.NONE);
Map<String, String> effectiveEnvironment = commandLine.getEffectiveEnvironment();
operation.setEnvironmentVariables(effectiveEnvironment);

GeneralCommandLine 中包括當(dāng)前 Java 進(jìn)程所有的環(huán)境變量,其他環(huán)境變量和 JVM 參數(shù)類似都被收集在 GradleExecutionSettings 中,Android Studio 會先將其他環(huán)境變量與 GeneralCommandLine 中的環(huán)境變量合并,然后配置給 BuildAction. 對于不進(jìn)行任何配置的默認(rèn) sync 行為,GradleExecutionSettings 中環(huán)境變量為空,全由 GeneralCommandLine 提供。

2.1.3 執(zhí)行 BuildAction

分析完 BuildAction 的配置邏輯后,接著來看 BuildAction 中具體做了哪些事。BuildAction 中的行為不再處于 Android Studio IDE 進(jìn)程了,而是在 Gradle 構(gòu)建進(jìn)程中執(zhí)行。IDE 通過 Gradle Tooling API 與 Gradle 交互時,主要媒介是 Gradle Model,BuildAction 中也不例外。BuildAction 的具體執(zhí)行邏輯見其實現(xiàn)類 ProjectImportAction 中的 execute 方法,我們只關(guān)注此方法中與 Gradle Model 相關(guān)的代碼:

public AllModels execute(final BuildController controller) {
...
fetchProjectBuildModels(wrappedController, isProjectsLoadedAction, myGradleBuild);
addBuildModels(wrappedController, myAllModels, myGradleBuild, isProjectsLoadedAction);
...
}

BuildAction 中調(diào)用 fetchProjectBuildModels 和 addBuildModels 方法獲取 Gradle Model。先來分析 fetchProjectBuildModels 方法,該方法中進(jìn)一步調(diào)用 getProjectModels 方法:

private List<Runnable> getProjectModels(@NotNull BuildController controller,
@NotNull final AllModels allModels,
@NotNull final BasicGradleProject project,
boolean isProjectsLoadedAction) {
...
Set<ProjectImportModelProvider> modelProviders = getModelProviders(isProjectsLoadedAction);
for (ProjectImportModelProvider extension : modelProviders) {
extension.populateProjectModels(controller, project, modelConsumer);
}
...
}

如上代碼所示,通過 ProjectImportModelProvider 的 populateProjectModels 方法進(jìn)一步去獲取 Gradle Model。BuildAction 的 addBuildModels 方法與此十分相似:

private void addBuildModels(@NotNull final ToolingSerializerAdapter serializerAdapter,
@NotNull BuildController controller,
@NotNull final AllModels allModels,
@NotNull final GradleBuild buildModel,
boolean isProjectsLoadedAction) {
Set<ProjectImportModelProvider> modelProviders = getModelProviders(isProjectsLoadedAction);
for (ProjectImportModelProvider extension : modelProviders) {
extension.populateBuildModels(controller, buildModel, modelConsumer);
}
...
}

可以看到同樣是交給了 ProjectImportModelProvider 去獲取 Gradle Model,不同的是,前者調(diào)用的 populateProjectModels,此處調(diào)用的是 populateBuildModels 方法,ProjectImportModelProvider 的作用就是生成 Gradle Model。ProjectImportModelProvider 同 JVM 參數(shù)和 Gradle 命令行參數(shù)一樣,都是由一系列 Gradle IDEA Plugin 擴(kuò)展提供,如下代碼所示:

for (GradleProjectResolverExtension resolverExtension = tracedResolverChain;
resolverExtension != null;
resolverExtension = resolverExtension.getNext()) {
...
ProjectImportModelProvider modelProvider = resolverExtension.getModelProvider();
if (modelProvider != null) {
projectImportAction.addProjectImportModelProvider(modelProvider);
}
ProjectImportModelProvider projectsLoadedModelProvider = resolverExtension.getProjectsLoadedModelProvider();
if (projectsLoadedModelProvider != null) {
projectImportAction.addProjectImportModelProvider(projectsLoadedModelProvider, true);
}
}

對于 Android 工程來說,重點關(guān)注 Android IDEA Plugin,它提供的 ProjectImportModelProvider 的實現(xiàn)類為 AndroidExtraModelProvider,該實現(xiàn)類的 populateProjectModels 方法如下:

override fun populateProjectModels(controller: BuildController,
projectModel: Model,
modelConsumer: ProjectImportModelProvider.ProjectModelConsumer) {
controller.findModel(projectModel, GradlePluginModel::class.java)
?.also { pluginModel -> modelConsumer.consume(pluginModel, GradlePluginModel::class.java) }
controller.findModel(projectModel, KaptGradleModel::class.java)
?.also { model -> modelConsumer.consume(model, KaptGradleModel::class.java) }
}

此方法中通過 Gradle Tooling API 獲取了 GradlePluginModel 和 KaptGradleModel 兩個 Gradle Model,GradlePluginModel 中提供了項目已應(yīng)用的 Gradle Plugin 信息;KaptGradleModel 提供了 Kotlin 注解處理相關(guān)信息。接著來看 populateBuildModels 方法:

override fun populateBuildModels(
controller: BuildController,
buildModel: GradleBuild,
consumer: ProjectImportModelProvider.BuildModelConsumer) {
populateAndroidModels(controller, buildModel, consumer)
populateProjectSyncIssues(controller, buildModel, consumer)
}

其中分別調(diào)用了 populateAndroidModels 和 populateProjectSyncIssues 方法,先來看 populateProjectSyncIssues 方法,它會進(jìn)一步通過 Gradle Tooling API 獲取 ProjectSyncIssues Gradle Model,用于收集 Sync 過程中出現(xiàn)的問題。再來分析 populateAndroidModels 方法,它在整個 Sync 過程中至關(guān)重要,其中通過 Gradle Tooling API 獲取了 Android 工程相關(guān)的 Gradle Model:

val androidModules: MutableList<AndroidModule> = mutableListOf()
buildModel.projects.forEach { gradleProject ->
findParameterizedAndroidModel(controller, gradleProject, AndroidProject::class.java)?.also { androidProject ->
consumer.consumeProjectModel(gradleProject, androidProject, AndroidProject::class.java)
val nativeAndroidProject = findParameterizedAndroidModel(controller, gradleProject, NativeAndroidProject::class.java)?.also {
consumer.consumeProjectModel(gradleProject, it, NativeAndroidProject::class.java)
}
androidModules.add(AndroidModule(gradleProject, androidProject, nativeAndroidProject))
}
}

如上代碼所示,分別獲取了由 Android Gradle Plugin 注冊的兩個 Gradle Model AndroidProject 和 NativeAndroidProject,AndroidProject 中包括 Android 應(yīng)用的 BuildType、Flavors、Variant、Dependency 等關(guān)鍵信息;NativeAndroidProject 中包括 NDK、NativeToolchain 等 Android C/C++ 項目相關(guān)信息。獲取了最關(guān)鍵的 AndroidProject 和 NativeAndroidProject 后,接著是對單 Variant Sync 的處理:

if (syncActionOptions.isSingleVariantSyncEnabled) {
chooseSelectedVariants(controller, androidModules, syncActionOptions)
}

怎么理解單 Variant Sync 呢?Variant 指 Android 應(yīng)用的產(chǎn)物變體,比如最簡單的 Debug 和 Release 版本應(yīng)用包。Variant 對應(yīng)于一套構(gòu)建配置,與項目源碼結(jié)構(gòu)、依賴列表相關(guān)聯(lián)。Android 應(yīng)用可能有多個 Variant,如果在 Sync 時構(gòu)造所有 Variant,整體耗時可能極長,所以 Android Studio Sync 默認(rèn)只會構(gòu)造一個 Variant,并支持 Variant 切換功能。如果啟用了單 Variant Sync,前面獲取 AndroidProject 時會傳入過濾參數(shù),告知 Android Gradle Plugin 構(gòu)造 AndroidProject 時無需構(gòu)造 Variant 信息。

2.2 Sync 流程梳理

2.2.1 Android Studio 視角

Android Studio Sync 流程的源碼龐大而繁雜,本文著重分析了從觸發(fā) Sync 入口到 Gradle 構(gòu)建結(jié)束的階段,后續(xù)還有對 Gradle 數(shù)據(jù)的處理,以及語言能力模塊基于 Gradle 數(shù)據(jù)進(jìn)行代碼索引的流程,而語言能力又是一個大型而復(fù)雜的模塊,本文就不再繼續(xù)展開。

上文源碼分析部分將觸發(fā) Sync 入口到 Gradle 構(gòu)建結(jié)束劃分為 Sync 功能入口及準(zhǔn)備、配置 BuildAction 以及執(zhí)行 BuildAction,整體流程如下圖所示:

圖片

2.2.2 Gradle 視角

上面都是以 IDE 視角去分析 Android Studio Sync,整體流程較復(fù)雜,接下來以 Gradle 視角去梳理 Sync 流程,著重關(guān)注 Gradle 側(cè)的行為:

圖片

如上圖,對于 Gradle 來說,Sync 流程中 Android Studio 會通過 Gradle Tooling API 從 Gradle 側(cè)獲取一系列所需的 Gradle Model,除 Gradle 自身外,Gradle Plugin 也可以提供自定義的 Gradle Model。另外 Sync 流程中 Gradle 會經(jīng)歷自身定義的生命周期,聚焦此視角梳理流程如下:

圖片

當(dāng)通過 BuildAction 獲取 Gradle Model 時會觸發(fā) Gradle 的構(gòu)建行為,Gradle 構(gòu)建會經(jīng)歷自身生命周期定義的 Initialization、Configuration 和 Execution 階段。

3. 總結(jié)

本文首先介紹了 Android Studio Sync 流程中各個角色的作用及聯(lián)系,對 Sync 有一個較清晰的整體認(rèn)識,然后從源碼角度深入分析從觸發(fā) Sync 入口到 Gradle 構(gòu)建結(jié)束的階段,并詳細(xì)解釋了 Gradle Model、BuildAction 等關(guān)鍵概念,最后分別從 Android Studio 視角和 Gradle 視角對 Sync 流程進(jìn)行了整體梳理。

通過對 Android Studio Sync 流程的深入分析,除了對 Sync 功能的實現(xiàn)原理深度掌握外,對其意義也有了更深的理解。Sync 是 Android Studio 定義的一個 IDE 準(zhǔn)備階段,在這個準(zhǔn)備階段中,需提前準(zhǔn)備好關(guān)鍵的 IDE 功能,而這些功能要達(dá)到可用狀態(tài),需要獲取其必需的數(shù)據(jù)?;谶@個角度,對 Sync 流程的優(yōu)化方向也有了一定啟發(fā):首先從產(chǎn)品層面出發(fā)考慮 Sync 階段的定義,不是開發(fā)者真正必需的功能都可以考慮省略或延后準(zhǔn)備;然后確認(rèn)必需功能所需的最小數(shù)據(jù)集,不必需的數(shù)據(jù)都可以省略;最后針對必需數(shù)據(jù),通過更高效的實現(xiàn)或緩存,找到最快的獲取方式。

4. 參考鏈接

https://mp.weixin.qq.com/s/cftj6WueoHlLh-So9REEXQ

https://developer.android.com/studio/intro

https://android.googlesource.com/platform/tools/base/+/studio-master-dev

https://plugins.jetbrains.com/developers

責(zé)任編輯:未麗燕 來源: 字節(jié)跳動技術(shù)團(tuán)隊
相關(guān)推薦

2022-09-01 08:08:35

Android移動操作系統(tǒng)

2021-09-16 06:44:04

Android進(jìn)階流程

2024-05-23 08:02:23

2017-05-03 17:00:16

Android渲染機(jī)制

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2020-07-21 08:26:08

SpringSecurity過濾器

2021-09-17 06:55:50

AndroidLayoutView

2021-09-30 07:36:51

AndroidViewDraw

2021-10-26 17:52:52

Android插件化技術(shù)

2013-09-22 14:57:19

AtWood

2009-09-25 09:14:35

Hibernate日志

2023-10-19 11:12:15

Netty代碼

2021-02-17 11:25:33

前端JavaScriptthis

2020-09-23 10:00:26

Redis數(shù)據(jù)庫命令

2017-01-10 08:48:21

2017-08-15 13:05:58

Serverless架構(gòu)開發(fā)運維

2019-06-25 10:32:19

UDP編程通信

2024-02-21 21:14:20

編程語言開發(fā)Golang

2021-09-15 07:31:33

Android窗口管理
點贊
收藏

51CTO技術(shù)棧公眾號