首頁/ 汽車/ 正文

深入理解 Android Studio Sync 流程

1。 初識 Sync

我們一般會把 Sync 理解為 Android Studio 的準備階段,包括解析工程配置資訊、下載遠端依賴到本地、更新程式碼索引等準備工作,當修改 gradle build 檔案後,需要重新 Sync 將 Gradle 構建配置資訊同步到 IDE,進而使 IDE 的功能及時應用新的構建配置,這些功能包括專案的 Gradle Task 列表展示、依賴資訊展示等等。Sync 是 Android Studio 中獨有的概念,當透過 Gradle 命令列程式構建 Android 應用時,只會經歷 Gradle 定義的 Initialization、Configuration 和 Execution 生命週期,根本沒有 Sync 的概念。Android Studio 的 Sync 階段涉及到 IDE、Gradle、Plugin 等多個角色,梳理清楚這些角色各自的作用和聯絡,才能清晰理解 Sync 的整體架構,下面分別來介紹這些角色:

深入理解 Android Studio Sync 流程

IDE

層面

:Android Studio 基於 IntelliJ IDEA 擴充套件而來,複用了 IntelliJ IDEA 強大的程式碼編輯器和開發者工具等 IDE 基礎能力,在此之上 Android Studio 提供了更多提高 Android 構建效率的功能,如 Android 模擬器、程式碼模版等等。另外 Android Studio 也將自身專業的 Android 應用開發能力反哺給 IntelliJ IDEA,以 Android IDEA Plugin 的形式,使 IntelliJ IDEA 支援 Android 應用開發,二者互相賦能,相輔相成。

Gradle

層面

:Gradle 是一個靈活而強大的開源構建系統,它除了提供跨平臺的可執行程式支援命令列執行 Gradle 構建外,還專門提供了 Gradle Tooling API 程式設計 SDK,供外部更方便、更緊密的將 Gradle 構建能力嵌入到 IDE 中,IntelliJ IDEA、Eclipse、VSCode 等 IDE 都採用了這種方式。在 Gradle 原始碼中也有專門服務於 IntelliJ IDEA、Eclipse 等 IDE 的程式碼模組,構建工具和 IDE 兩個角色之間同樣是互相賦能,強強聯合。

Plugin 層面

:Plugin 層面包括 Android IDEA Plugin 和 Android Gradle Plugin,Android IDEA Plugin 為 IntelliJ IDEA/Android Studio 拓展了 Android 應用開發能力;Android Gradle Plugin 為 Gradle 拓展了 Android 應用構建能力。谷歌透過這兩個 Plugin 將現代成熟優秀的 IDE 開發能力和構建工具聯合在一起為 Android 所用,相比於早期 Eclipse 加 ANT 構建的開發方式,大幅提升了 Android 應用開發效率和體驗。

2。 Sync 流程分析

瞭解了 Sync 階段涉及到的角色以及它們之間的關係後,接下來深入 Android Studio 原始碼,從程式碼層面梳理清楚 Sync 的關鍵流程。

2.1 Android Studio 原始碼分析

2.1.1 功能入口及準備

在 Android Studio 中觸發 Sync 操作後,會從最上層的入口類 GradleSyncInvoker 呼叫到實際負責解析 Gradle 構建資訊的 GradleProjectResolver,呼叫鏈如下圖所示:

深入理解 Android Studio Sync 流程

呼叫過程中涉及到的關鍵類:

GradleSyncInvoker

:觸發 Sync 的入口類,在 Android Studio 多處需要執行 Sync 的地方,都是透過呼叫此類的 requestProjectSync 方法來觸發的

ExternalSystemUtil

:GradleSyncInvoker、GradleSyncExecutor 等類是專門針對 Sync 功能的封裝,Sync 是 Android Studio 中獨有的操作,在 IntelliJ IDEA 中並沒有 Sync 的概念,IntelliJ IDEA 透過 [Reload All Gradle Projects] 操作來觸發解析工程的 Gradle 構建資訊,直接從 ExternalSystemUtil 類開始執行

GradleProjectResolver

:負責具體執行 Sync,其中 resolveProjectInfo 方法是具體執行 Sync 邏輯的地方,該方法的定義如下:

public DataNode resolveProjectInfo( @NotNull ExternalSystemTaskId id, @NotNull String projectPath, boolean isPreviewMode, @Nullable S settings, @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException, IllegalArgumentException, IllegalStateException

id

:本次 Sync 操作的唯一標識,後續可透過呼叫 GradleProjectResolver 中的 cancelTask 方法取消本次 Sync 任務

projectPath

:工程絕對路徑

settings

:Sync 工程的配置引數,可設定 Java 版本等

listener

:用於監聽此次 Sync 的過程及結果

isPreviewMode

:是否要啟用預覽模式,Android Studio 首次開啟未知來源專案時,會讓開發者選擇專案的開啟方式,如下圖,若選擇 [Stay in Safe Mode] 則會以“預覽模式”執行專案,表示 IDE 僅可以瀏覽專案的原始碼,不會執行或解析任何構建任務和指令碼

深入理解 Android Studio Sync 流程

進入 GradleProjectResolver 的 resolveProjectInfo 方法中後,首先會對預覽模式進行處理,如下程式碼所示,如果是預覽模式,則會簡單構造出對應的工程資料結構後立馬返回,不進行任何的解析行為:

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

使用 Android Studio/IntelliJ IDEA 開啟工程時,除了指定工程所在根目錄,還可以指定 Gradle 配置檔案,這個邏輯在原始碼中也有所體現:

if (projectPathFile。isFile() && projectPath。endsWith(GradleConstants。EXTENSION) && projectPathFile。getParent() != null) { projectDir = projectPathFile。getParent(); if (settings != null) { List arguments = settings。getArguments(); if (!arguments。contains(“-b”) && !arguments。contains(“——build-file”)) { settings。withArguments(“-b”, projectPath); } }} else { projectDir = projectPath;}

可以看到如果開啟的不是工程根目錄而是配置檔案,那就會把該配置檔案所在的目錄作為工程路徑,並且會透過 ——build-file 引數來指定配置檔案,後續會傳入到 Gradle 中。對工程初步處理後,接著透過 Gradle Tooling API 獲取工程的 BuildEnvironment:

ModelBuilder modelBuilder = connection。model(BuildEnvironment。class);

呼叫 Gradle Tooling API 時其內部會自動下載專案配置版本的 Gradle,也就是說,上面程式碼中獲取 BuildEnvironment Model 的呼叫本身確保了 Gradle 的下載,即將 /grade/wrapper/gradle-wrapper。properties 裡 distributionUrl 指定的 Gradle 下載到 GRADLE_HOME/wrapper/dists 下。

BuildEnvironment 是 Gradle 提供的 Gradle Model,Gradle Model 是一個非常重要的概念,當 IDE 透過 Gradle Tooling API 和 Gradle 互動時,傳輸的就是各種各樣的 Model,比如 Gradle 自帶的 GradleProject

BuildEnvironment 等,另外也可以在 Gradle Plugin 中透過 ToolingModelBuilderRegistry 註冊自定義的 Model,如 Android Gradle Plugin 中註冊了 AndroidProject Model,那就可以透過 Gradle Tooling API 獲取 Android 專案特有的 AndroidProject Model,從而獲取由 Android Gradle Plugin 提供的 Android 應用相關工程資訊。

2。1。2 配置 BuildAction

繼續分析 Android Studio Sync 原始碼,接下來構造了一個 ProjectImportAction,它實現了 Gradle Tooling API 中的 BuildAction 介面,BuildAction 定義如下:

/*** An action that executes against a Gradle build and produces a result of type { @code T}。 * @param The type of result produced by this action。*/public interface BuildAction extends Serializable {T execute(BuildController controller);}

BuildAction 是即將傳入到 Gradle 構建程序中執行的行為,並且可將結果資料序列化返回給呼叫方。這個 BuildAction至關重要,它是實際和 Gradle 通訊的地方,其中實現了組織生成工程資訊、下載依賴等功能,是 Sync 流程中的核心邏輯。BuildAction 再配合 Gradle Tooling API 中的 BuildActionExecuter,就可以將 BuildAction 交由 Gradle 觸發執行了,在執行之前,需先透過 BuildActionExecuter 配置 JVM 引數、Gradle 命令列引數以及環境變數等構建資訊:

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()) { // collect extra JVM arguments provided by gradle project resolver extensions ParametersList parametersList = new ParametersList(); for (Pair jvmArg : extension。getExtraJvmArgs()) { parametersList。addProperty(jvmArg。first, jvmArg。second); } executionSettings。withVmOptions(parametersList。getParameters()); } // collect extra command-line arguments executionSettings。withArguments(extension。getExtraCommandLineArgs()); });}

上述程式碼較多,我們先只關注於 GradleExecutionSettings 的 withArgument 和 withVmOptions 方法,它們分別負責收集 Gradle 命令列引數和 JVM 引數。從程式碼中可以看出,這些 Gradle 命令列引數及 JVM 引數大多數是從 extension 中收集而來,這裡的 extension 是指 IntelliJ IDEA Plugin 中的擴充套件,和擴充套件點結合使用,可擴充套件 IntelliJ IDEA 平臺特性或其他 IntelliJ IDEA Plugin 的功能特性,舉個例子,Gradle IDEA Plugin 提供了工程解析的擴充套件點:

Android IDEA Plugin 中實現了此擴充套件點,提供了 Android 工程解析的拓展:

……

接著來看 BuildAction 的引數配置邏輯,最終設定 JVM 引數的地方在 GradleExecutionHelper 的 prepare 方法中:

List jvmArgs = settings。getJvmArguments();BuildEnvironment buildEnvironment = getBuildEnvironment(connection, id, listener, (CancellationToken)null, settings);if (!jvmArgs。isEmpty()) { // merge gradle args e。g。 defined in gradle。properties Collection merged; if (buildEnvironment != null) { // the BuildEnvironment jvm arguments of the main build should be used for the ‘buildSrc’ import // to avoid spawning of the second gradle daemon BuildIdentifier buildIdentifier = getBuildIdentifier(buildEnvironment); List buildJvmArguments = buildIdentifier == null || “buildSrc”。equals(buildIdentifier。getRootDir()。getName()) ? ContainerUtil。emptyList() : buildEnvironment。getJava()。getJvmArguments(); merged = mergeBuildJvmArguments(buildJvmArguments, jvmArgs); } else { merged = jvmArgs; } List filteredArgs = ContainerUtil。mapNotNull(merged, s -> StringUtil。isEmpty(s) ? null : s); operation。setJvmArguments(ArrayUtilRt。toStringArray(filteredArgs));}

如上程式碼所示,JVM 引數的配置邏輯很簡單:將之前從一系列 GradleProjectResolve 擴充套件中收集的、存放在 GradleExecutionSettings 中的 JVM 引數和 BuildEnvironment 中的 JVM 引數合併,然後呼叫 BuildActionExecuter 的 setJvmArguments 方法,將 JVM 引數設定給 BuildAction。Gradle 命令列引數同樣是在 GradleExecutionHelper 的 prepare 方法中配置:

。。。List 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); // filter nulls and empty strings 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 命令列引數如下:

引數

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 擴充套件: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 擴充套件:org。jetbrains。kotlin。idea。gradleJava。scripting。importing。KotlinDslScriptModelResolver

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

Kotlin IDEA Plugin 擴充套件:org。jetbrains。kotlin。idea。gradleJava。scripting。importing。KotlinDslScriptModelResolver

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

Kotlin IDEA Plugin 擴充套件:org。jetbrains。kotlin。idea。gradleJava。configuration。KotlinMPPGradleProjectResolver

-Pkotlin。mpp。enableIntransitiveMetadataConfiguration=true

重點關注其中的 ——init-script 命令列引數,它可以指定一個初始化指令碼,初始化指令碼會在專案構建指令碼之前執行。初始化指令碼是 Gradle 提供的一個非常靈活的機制,除了命令列配置,還可以將初始化指令碼命名為 init。gradle 放置到 USER_HOME/。gradle/ 下進行配置。初始化指令碼允許自定義所有專案的構建邏輯,比如定義特定機器上所有專案的 JDK 路徑等環境資訊。在上述 Kotlin App Demo 工程中,Android IDEA Plugin 擴充套件配置了一個初始化指令碼,內容如下:

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}

初始化指令碼中對所有專案應用了 AndroidStudioToolingPlugin 外掛,此外掛中透過 ToolingModelBuilderRegistry 註冊了 AdditionalClassifierArtifactsModel,這個 Gradle Model 中實現了下載依賴 sources 和 javadoc 的功能:

/*** Model Builder for [AdditionalClassifierArtifactsModel]。** This model builder downloads sources and javadoc for components specifies in parameter, and returns model* [AdditionalClassifierArtifactsModel], which contains the locations of downloaded jar files。*/class AdditionalClassifierArtifactsModelBuilder : ParameterizedToolingModelBuilder {。。。

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

分析完 BuildAction 的 JVM 引數和 Gradle 命令列引數配置流程後,最後來看 BuildAction 環境變數的配置,最終配置的地方在 GradleExecutionHelper 的 setupEnvironment 方法中:

GeneralCommandLine commandLine = new GeneralCommandLine();commandLine。withEnvironment(settings。getEnv());commandLine。withParentEnvironmentType( settings。isPassParentEnvs() ? GeneralCommandLine。ParentEnvironmentType。CONSOLE : GeneralCommandLine。ParentEnvironmentType。NONE);Map effectiveEnvironment = commandLine。getEffectiveEnvironment();operation。setEnvironmentVariables(effectiveEnvironment);

GeneralCommandLine 中包括當前 Java 程序所有的環境變數,其他環境變數和 JVM 引數類似都被收集在 GradleExecutionSettings 中,Android Studio 會先將其他環境變數與 GeneralCommandLine 中的環境變數合併,然後配置給 BuildAction。 對於不進行任何配置的預設 sync 行為,GradleExecutionSettings 中環境變數為空,全由 GeneralCommandLine 提供。

2.1.3 執行 BuildAction

分析完 BuildAction 的配置邏輯後,接著來看 BuildAction 中具體做了哪些事。BuildAction 中的行為不再處於 Android Studio IDE 程序了,而是在 Gradle 構建程序中執行。IDE 透過 Gradle Tooling API 與 Gradle 互動時,主要媒介是 Gradle Model,BuildAction 中也不例外。BuildAction 的具體執行邏輯見其實現類 ProjectImportAction 中的 execute 方法,我們只關注此方法中與 Gradle Model 相關的程式碼:

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

BuildAction 中呼叫 fetchProjectBuildModels 和 addBuildModels 方法獲取 Gradle Model。先來分析 fetchProjectBuildModels 方法,該方法中進一步呼叫 getProjectModels 方法:

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

如上程式碼所示,透過 ProjectImportModelProvider 的 populateProjectModels 方法進一步去獲取 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 modelProviders = getModelProviders(isProjectsLoadedAction); for (ProjectImportModelProvider extension : modelProviders) { extension。populateBuildModels(controller, buildModel, modelConsumer); } 。。。}

可以看到同樣是交給了 ProjectImportModelProvider 去獲取 Gradle Model,不同的是,前者呼叫的 populateProjectModels,此處呼叫的是 populateBuildModels 方法,ProjectImportModelProvider 的作用就是生成 Gradle Model。ProjectImportModelProvider 同 JVM 引數和 Gradle 命令列引數一樣,都是由一系列 Gradle IDEA Plugin 擴充套件提供,如下程式碼所示:

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 工程來說,重點關注 Android IDEA Plugin,它提供的 ProjectImportModelProvider 的實現類為 AndroidExtraModelProvider,該實現類的 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 中提供了專案已應用的 Gradle Plugin 資訊;KaptGradleModel 提供了 Kotlin 註解處理相關資訊。接著來看 populateBuildModels 方法:

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

其中分別呼叫了 populateAndroidModels 和 populateProjectSyncIssues 方法,先來看 populateProjectSyncIssues 方法,它會進一步透過 Gradle Tooling API 獲取 ProjectSyncIssues Gradle Model,用於收集 Sync 過程中出現的問題。再來分析 populateAndroidModels 方法,它在整個 Sync 過程中至關重要,其中透過 Gradle Tooling API 獲取了 Android 工程相關的 Gradle Model:

val androidModules: MutableList = 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 應用的 BuildType、Flavors、Variant、Dependency 等關鍵資訊;NativeAndroidProject 中包括 NDK、NativeToolchain 等 Android C/C++ 專案相關資訊。獲取了最關鍵的 AndroidProject 和 NativeAndroidProject 後,接著是對單 Variant Sync 的處理:

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

怎麼理解單 Variant Sync 呢?Variant 指 Android 應用的產物變體,比如最簡單的 Debug 和 Release 版本應用包。Variant 對應於一套構建配置,與專案原始碼結構、依賴列表相關聯。Android 應用可能有多個 Variant,如果在 Sync 時構造所有 Variant,整體耗時可能極長,所以 Android Studio Sync 預設只會構造一個 Variant,並支援 Variant 切換功能。如果啟用了單 Variant Sync,前面獲取 AndroidProject 時會傳入過濾引數,告知 Android Gradle Plugin 構造 AndroidProject 時無需構造 Variant 資訊。

2.2 Sync 流程梳理

2.2.1 Android Studio 視角

Android Studio Sync 流程的原始碼龐大而繁雜,本文著重分析了從觸發 Sync 入口到 Gradle 構建結束的階段,後續還有對 Gradle 資料的處理,以及語言能力模組基於 Gradle 資料進行程式碼索引的流程,而語言能力又是一個大型而複雜的模組,本文就不再繼續展開。

上文原始碼分析部分將觸發 Sync 入口到 Gradle 構建結束劃分為 Sync 功能入口及準備、配置 BuildAction 以及執行 BuildAction,整體流程如下圖所示:

深入理解 Android Studio Sync 流程

2.2.2 Gradle 視角

上面都是以 IDE 視角去分析 Android Studio Sync,整體流程較複雜,接下來以 Gradle 視角去梳理 Sync 流程,著重關注 Gradle 側的行為:

深入理解 Android Studio Sync 流程

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

深入理解 Android Studio Sync 流程

當透過 BuildAction 獲取 Gradle Model 時會觸發 Gradle 的構建行為,Gradle 構建會經歷自身生命週期定義的 Initialization、Configuration 和 Execution 階段。

3。 總結

本文首先介紹了 Android Studio Sync 流程中各個角色的作用及聯絡,對 Sync 有一個較清晰的整體認識,然後從原始碼角度深入分析從觸發 Sync 入口到 Gradle 構建結束的階段,並詳細解釋了 Gradle Model、BuildAction 等關鍵概念,最後分別從 Android Studio 視角和 Gradle 視角對 Sync 流程進行了整體梳理。

透過對 Android Studio Sync 流程的深入分析,除了對 Sync 功能的實現原理深度掌握外,對其意義也有了更深的理解。Sync 是 Android Studio 定義的一個 IDE 準備階段,在這個準備階段中,需提前準備好關鍵的 IDE 功能,而這些功能要達到可用狀態,需要獲取其必需的資料。基於這個角度,對 Sync 流程的最佳化方向也有了一定啟發:首先從產品層面出發考慮 Sync 階段的定義,不是開發者真正必需的功能都可以考慮省略或延後準備;然後確認必需功能所需的最小資料集,不必需的資料都可以省略;最後針對必需資料,透過更高效的實現或快取,找到最快的獲取方式。

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

5。 關於我們

我們是位元組跳動終端技術團隊(Client Infrastructure)下的 Developer Tools 團隊,負責打造公司範圍內,面向不同業務場景的研發工具,提升移動應用研發效率。目前急需尋找 Android 移動研發工程師 / iOS 移動研發工程師 / 服務端研發工程師。瞭解更多資訊請聯絡:wangyinghao。ahab@bytedance。com,郵件主題 簡歷-姓名-求職意向-期望城市-電話。

相關文章

頂部