Last active
October 5, 2025 01:02
-
-
Save sickworm/ee2d64b12a770a88e7c860ae4058634a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import com.android.build.gradle.internal.services.BuildServicesKt | |
| import com.android.build.gradle.options.ProjectOptions | |
| import com.android.build.gradle.options.ProjectOptionService | |
| import com.android.build.gradle.options.StringOption | |
| import com.android.builder.errors.EvalIssueException | |
| import com.android.builder.errors.IssueReporter | |
| import com.google.common.collect.ImmutableMap | |
| import org.jetbrains.annotations.NotNull | |
| import java.lang.reflect.Field | |
| /** | |
| * 目的:动态设置 gradle.properties 里的 android.aapt2FromMavenOverride=/Users/sickworm/Library/Android/sdk/build-tools/35.0.1/aapt2 | |
| * gradle.properties 不支持动态路径 | |
| */ | |
| // 1. 拿到 35.0.1 aapt2 的路径 | |
| // 更推荐的方式:直接拷贝 $sdkDir/build-tools/35.0.1/aapt2 到工程,这样就不用每个开发安装一次了(需要分别内置 windows,linux,mac) | |
| def sdkDir = com.android.build.gradle.internal.SdkLocator.getSdkDirectory(project.rootDir, new IssueReporter() { | |
| @Override | |
| boolean hasIssue(@NotNull IssueReporter.Type type) { | |
| return false | |
| } | |
| @Override | |
| protected void reportIssue(@NotNull IssueReporter.Type type, @NotNull IssueReporter.Severity severity, @NotNull EvalIssueException e) { | |
| } | |
| }) | |
| println("sdkDir: $sdkDir") | |
| if (sdkDir == null || !sdkDir.exists()) { | |
| System.err.println("Android sdkDir 找不到,退出替换") // 这里不应该没有,先不直接报错,先跑起来,避免首次初始化等部分场景读不到 | |
| return | |
| } | |
| def aapt2Path = "$sdkDir/build-tools/35.0.1/aapt2" | |
| if (! new File(aapt2Path).exists()) { | |
| def message = "编译检查:找不到 $aapt2Path,请先安装 build-tools 35.0.1。方法:Android Studio -> Tools -> SDK Manager -> SDK Tools -> 安装 build-tools 35.0.1。" | |
| System.err.println(message) | |
| System.err.println(message) | |
| System.err.println(message) | |
| throw new IllegalArgumentException(message) | |
| } | |
| // 2. 反射设置进去 | |
| new ProjectOptionService.RegistrationAction(project).execute().get() // 触发 ProjectOptionService 创建。正常是在 apply com.android.library 时懒加载创建 | |
| def buildServiceRegistry = project.getGradle().getSharedServices() | |
| Object byName = buildServiceRegistry.getRegistrations().getByName(BuildServicesKt.getBuildServiceName(ProjectOptionService.class)) | |
| if (byName == null) { | |
| throw new NullPointerException("null cannot be cast to non-null type org.gradle.api.services.BuildServiceRegistration<ServiceT of com.android.build.gradle.internal.services.BuildServicesKt.getBuildService, *>"); | |
| } | |
| def service = byName.getService() | |
| ProjectOptions projectOptions = service.get().getProjectOptions() | |
| println("projectOptions $projectOptions") | |
| Field stringOptionValues = projectOptions.getClass().getDeclaredField("stringOptionValues") | |
| stringOptionValues.setAccessible(true) | |
| ImmutableMap<StringOption, ProjectOptions.OptionValue<StringOption, String>> value = stringOptionValues.get(projectOptions) | |
| // 2.2 找到 AAPT2_FROM_MAVEN_OVERRIDE | |
| value.entrySet().forEach { entry -> | |
| def getMethod = entry.value.getClass().getDeclaredMethod("getValueForUseAtConfiguration") | |
| getMethod.setAccessible(true) | |
| // AAPT2_FROM_MAVEN_OVERRIDE=/Users/sickworm/Library/Android/sdk/build-tools/35.0.1/aapt2 | |
| if (entry.key.toString() == "AAPT2_FROM_MAVEN_OVERRIDE") { | |
| println("projectOptions ${entry.key}=${getMethod.invoke(entry.value)}") | |
| def field = entry.value.getClass().getDeclaredField("option") // StringOption | |
| field.setAccessible(true) | |
| StringOption stringOption = field.get(entry.value) | |
| def mockStringOption = new com.android.build.gradle.options.Option<String>() { | |
| @Override | |
| String getDefaultValue() { | |
| return aapt2Path | |
| } | |
| @Override | |
| String getPropertyName() { | |
| return stringOption.getPropertyName() | |
| } | |
| @Override | |
| com.android.build.gradle.options.Option.Status getStatus() { | |
| return stringOption.getStatus() | |
| } | |
| @Override | |
| String parse(@NotNull Object o) { | |
| return aapt2Path | |
| } | |
| } | |
| field.set(entry.value, mockStringOption) | |
| println("projectOptions after ${entry.key}=${getMethod.invoke(entry.value)}") | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment