一、源码
可以在 Google Git 中找到 Launcher3 源码。
直接拉取 Launcher3 代码:
git clone https://android.googlesource.com/platform/packages/apps/Launcher3
你会看到很多分支,可以选你自己所需要的分支:
比如 android 14:
二、Gradle
切到对应分支后,我们用 Android Studio 打开 Launcher3 工程,等待自动构建,你会看到如下报错信息:
2.1 添加 Gradle 构建工具
Launcher3 默认添加了一些 build.gradle 文件,而这些文件是需要 Gradle 构建工具的,对于像 Launcher 这种采用 bp 源码编译的应用,默认是没有的,我们可以从别处 copy 一个构建工具过来(随你是你已有项目还是新建的项目),如下:
现在我们再 sync 一下:
2.2 优化 build.gradle
先前 build.gradle 无法解析的报错信息没有了,出现了新的报错:
Project with path ':UiTestsLibLauncher' could not be found in root project 'Gradle_Launcher3_AOSP'.
我们工程里面是没有 UiTestsLibLauncher
的,所以可以直接在 build.gradle 里面去掉:
📄 build.gradledependencies {implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"implementation "androidx.preference:preference:${ANDROID_X_VERSION}"implementation project(':IconLoader')// implementation project(':UiTestsLibLauncher')withQuickstepImplementation project(':SharedLibWrapper')...
}
再 sync:
报错原因是:protoc 编译器的版本没有指定多少,我们可以直接指定:
📄 build.gradleprotobuf {// Configure the protoc executableprotoc {// 指定 protoc 编译器的版本artifact = "com.google.protobuf:protoc:3.22.3"}generateProtoTasks {...}
}
再 sync:
此时 Gradle 开始 Download 所需要的各种库,我们等待它同步完成即可,最终会是下面这个样子:
此时,我们其实已经成功搭建号了 Launcher3 Gradle 编译所需要的环境!但其实,这只是万里长征的第一步,因为如果你想成功 build 出 apk,还有很多需要修改的地方。
三、第一阶段报错修复
现在我们来 build 一下 APK 试试:
开始报错:
我们看看在哪里用到的:
3.1 AGP 插件
我们需要修改下构建工具,不使用 Build Tools,改成 Android Gradle Plugin (AGP) ,如下:
3.2 无用引用
继续 build APK:
老样子,找不到 IconLoader
和 SharedWrapper
,我们直接注掉:
📄 build.gradledependencies {implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"implementation "androidx.preference:preference:${ANDROID_X_VERSION}"// implementation project(':IconLoader')// implementation project(':UiTestsLibLauncher')// withQuickstepImplementation project(':SharedLibWrapper')...
}
同时我们需要修改 settings.gradle 文件:
📄 settings.gradle// 不需要了,直接注释掉//include ':IconLoader'
//project(':IconLoader').projectDir = new File(rootDir, 'iconloaderlib')//include ':SharedLibWrapper'
//project(':SharedLibWrapper').projectDir = new File(rootDir, 'SharedLibWrapper')
3.3 Recycleview 库
继续 build APK:
这里的报错就很有意思:
implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"
ANDROID_X_VERSION=1+
从官方仓库拉的最新的 RecyeleView 库的最新版本是 1.4.0-beta01,本来之前所有的 Launcher3 项目是不会抱这个错的,但是就在最近,官方加了一个说明:
所以,我们就直接指定 RecycleView 的版本号吧:
implementation "androidx.recyclerview:recyclerview:1.4.0-alpha01"
3.4 资源报错
继续 build APK:
你会发现一堆报错,不急,我们慢慢改,比如以下报错:
1: Task failed with an exception.
-----------
* What went wrong:
Some problems were found with the configuration of task ':extractAospProto' (type 'ProtobufExtract').- In plugin 'com.google.protobuf' type 'com.google.protobuf.gradle.ProtobufExtract' property 'destDir' is missing an input or output annotation.Reason: A property without annotation isn't considered during up-to-date checking.Possible solutions:1. Add an input or output annotation.2. Mark it as @Internal.Please refer to https://docs.gradle.org/7.4/userguide/validation_problems.html#missing_annotation for more details about this problem.- In plugin 'com.google.protobuf' type 'com.google.protobuf.gradle.ProtobufExtract' property 'isTest' is missing an input or output annotation.Reason: A property without annotation isn't considered during up-to-date checking.Possible solutions:1. Add an input or output annotation.2. Mark it as @Internal.Please refer to https://docs.gradle.org/7.4/userguide/validation_problems.html#missing_annotation for more details about this problem.
我们需要升级 protobuf-gradle-plugin 插件版本:
📄 gradle.properties# PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.8.8
PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.9.4
继续 build APK:
这个报错表明你的项目中存在重复的类定义,这是因为不同版本的 Kotlin 标准库依赖被引入到你的项目中。具体来说,这些重复类来自以下两个依赖项:
1. org.jetbrains.kotlin:kotlin-stdlib:1.8.22
2. org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.0 和 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0
解决办法:
📄 build.gradlebuildscript {repositories {mavenCentral()google()}dependencies {classpath 'com.android.tools.build:gradle:7.3.1'classpath GRADLE_CLASS_PATHclasspath PROTOBUF_CLASS_PATH// 1. 声明 Kotlin Gradle 插件的依赖classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"}
}apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
apply plugin: 'kotlin-android' // 2. 应用 Kotlin Android 插件
apply plugin: 'kotlin-kapt' // 3. 应用 Kotlin Kapt 插件android {...// 4. 设置 Kotlin 编译器的选项kotlinOptions {// 设置 Kotlin 代码编译后生成的字节码目标版本为 Java 8。// 即使你使用的是较新的 JDK(例如 JDK 17),编译后的字节码也会兼容 Java 8jvmTarget = '1.8'freeCompilerArgs = ["-Xjvm-default=all"]}
}
经过如上 4 步修改,即可编译过,我们继续 Build APK,修复其它报错:
我们定位到代码里,比如:materialColorPrimaryFixedDim
:
是标红的,改法很简单,去掉 androidprv:
:
<color name="work_turn_on_stroke">?attr/colorAccentSecondaryVariant</color>
<color name="work_fab_bg_color">?attr/materialColorPrimaryFixedDim</color>
<color name="work_fab_icon_color">?attr/materialColorOnPrimaryFixed</color>
其它的资源报错类似的(?androidprv:attr/***),一并按照这个方式修改,修改完后,我们继续 Build APK:
这里的报错涉及到好几个修改点,我们一个个解决:
- 先来看:
colorAccentPrimaryVariant
和colorAccentSecondaryVariant
找不到。
📄 res/values/attrs.xml<!-- 添加, start -->
<attr name="colorAccentPrimaryVariant" format="color"/>
<attr name="colorAccentSecondaryVariant" format="color"/>
<!-- 添加, end --><attr name="materialColorOnSecondaryFixedVariant" format="color" />
<attr name="materialColorOnTertiaryFixedVariant" format="color" />
<attr name="materialColorSurfaceContainerLowest" format="color" />
- 再来看主题错误:
error: resource style/Theme.MaterialComponents.DayNight (aka com.android.launcher3:style/Theme.MaterialComponents.DayNight) not found.
com.android.launcher3-mergeAospWithQuickstepDebugResources-28:/values-v31/values-v31.xml:126: error: style attribute 'attr/elevationOverlayColor (aka com.android.launcher3:attr/elevationOverlayColor)' not found.
com.android.launcher3-mergeAospWithQuickstepDebugResources-28:/values-v31/values-v31.xml:127: error: style attribute 'attr/elevationOverlayEnabled (aka com.android.launcher3:attr/elevationOverlayEnabled)' not found.
我们需要在 build.gradle 里面添加 material 依赖:
📄 build.gradledependencies {implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"// noinspection GradleDependencyimplementation "androidx.recyclerview:recyclerview:1.4.0-alpha01"implementation "androidx.preference:preference:${ANDROID_X_VERSION}"// 添加 material 依赖implementation 'com.google.android.material:material:1.11.0'// implementation project(':IconLoader')// implementation project(':UiTestsLibLauncher')// withQuickstepImplementation project(':SharedLibWrapper')...
}
- 最后来看
disabledIconAlpha
、loadingIconColor
找不到的问题:
这两个属性是 iconloaderlib 库中的,它是 Android 系统和应用程序中处理图标加载和渲染的一个库。它在 Launcher 中用于加载、缓存、缩放和渲染应用程序图标,以确保在不同屏幕分辨率和设备环境下显示一致的图标质量。
3.5 iconloaderlib
现在我们是缺失这个库的,我们看看如何已模块的方式引进来(这样我们可以直接修改它的代码,Launcher 调试更方便)。
按照这个路径找到 iconloaderlib 包,直接 copy 到 Launcher 工程中,并且在工程的 build.gradle 里面进行一些配置:
现在我们继续 Build APK:
资源重复问题,如下:
📄 iconloaderlib/res/values/dimens<dimen name="profile_badge_size">24dp</dimen>📄 res/values/dimens<dimen name="profile_badge_size">24dp</dimen>
我们只需要把多余的删除即可,继续 Build APK:
恭喜你,到这里,我们就进入第二阶段(实际代码 API 调用错误)的报错修复之路了。
四、第二阶段报错修复
具体代码的报错信息,我就不给大家一个个分析了,究其原因就是缺了很多依赖库(Android.bp 里面引用的那些),所以这里直接告诉大家需要怎么做即可。
4.1 animationlib
和 iconloaderlib 一样,找到 animationlib 的包,copy 到 Launcher 工程:
并且也需要在 build.gradle 里面配置:
📄 build.gradlejava.srcDirs = ['src','src_plugins','iconloaderlib/src','iconloaderlib/src_full_lib','animationlib/src'] // 加进来
4.2 tests
📄 build.gradlejava.srcDirs = ['src','src_plugins','iconloaderlib/src','iconloaderlib/src_full_lib','animationlib/src','tests/shared'] // 加进来// 注释掉 test 模块其他资源引用
// androidTest {
// res.srcDirs = ['tests/res']
// java.srcDirs = ['tests/src', 'tests/tapl']
// manifest.srcFile "tests/AndroidManifest-common.xml"
// }
//
// androidTestDebug {
// manifest.srcFile "tests/AndroidManifest.xml"
// }
4.3 SystemUISharedLib
在 out/soong/.intermediates/frameworks/base/packages/SystemUI/shared/SystemUISharedLib/android_common/combined 下
拷贝 SystemUISharedLib.jar 并引入到 libs:
修改工程 build.gradle 配置:
📄 build.gradledependencies {implementation fileTree(dir: "libs", include: ['*.jar', '*.aar'])
}
4.4 SystemUIUnfoldLib
在 out/soong/.intermediates/frameworks/base/packages/SystemUI/unfold/SystemUIUnfoldLib/android_common/combined 下
拷贝 SystemUIUnfoldLib.jar 并引入到 libs:
4.5 SystemUIAimationLib
copy frameworks/base/packages/SystemUI/animation/ 目录的代码到 Launcher 工程,我们定一一个模块名为:SystemUIAimationLib,如下:
4.6 PluginCoreLib
在 out/soong/.intermediates/frameworks/base/packages/SystemUI/plugin_core/PluginCoreLib/android_common/javac 下
拷贝 PluginCoreLib 并引入到 libs:
4.7 core-animation
引入 androidx.core:core-animation 库到 Android 项目中:
📄 build.gradledependencies {implementation 'androidx.core:core-animation:1.0.0-rc01'
}
4.8 注释标红代码
📄 src/com/android/launcher3/logging/StartupLatencyLoggerif (!BuildConfig.IS_STUDIO_BUILD && !isInTest) {return true
}有两处,全部注释掉!
4.9 protobuf
build.gradle 添加依赖:
📄 build.gradledependencies {sourceSets {main {...proto {// srcDirs = ['protos/', 'protos_overrides/']// 修改srcDirs = ['protos/','quickstep/protos_overrides/']}}...implementation 'com.google.protobuf:protobuf-javalite:3.22.3'
}
4.10 JDK 调整
📄 build.gradledependencies {compileOptions {// sourceCompatibility JavaVersion.VERSION_1_8// targetCompatibility JavaVersion.VERSION_1_8sourceCompatibility JavaVersion.VERSION_17targetCompatibility JavaVersion.VERSION_17}kotlin {jvmToolchain(17)}
}
4.11 turbine 包引入
之前我们引入了 SystemUISharedLib 和 SystemUIUnfoldLib 的 jar 包,但他们是 combined 包下的,我们需要另外再引入 turbine-combined 下的 jar 包,如下:
在 out/soong/.intermediates/frameworks/base/packages/SystemUI/shared/SystemUISharedLib/android_common/turbine-combined 下
拷贝 SystemUISharedLib.jar 并引入到 libs,由于包名相同,我们重命名为:SystemUISharedLib-turbine.jar
在 out/soong/.intermediates/frameworks/base/packages/SystemUI/unfold/SystemUIUnfoldLib/android_common/turbine-combined 下
拷贝 SystemUIUnfoldLib.jar 并引入到 libs,由于包名相同,我们重命名为:SystemUIUnfoldLib-turbine.jar
4.12 SystemUI-statsd
在 out/soong/.intermediates/frameworks/base/packages/SystemUI/shared/SystemUI-statsd/android_common/javac 下
拷贝 SystemUI-statsd.jar 并引入到 libs:
4.13 Slice 库
添加依赖:
📄 build.gradledependencies {implementation 'androidx.slice:slice-core:1.0.0'implementation 'androidx.slice:slice-builders:1.0.0'
}
4.14 viewcapturelib
和 iconloaderlib 一样的路径,我们找到 viewcapturelib 包,直接 copy 到 Launcher 工程中,并且在工程的 build.gradle 里面进行一些配置:
到这里,我们再次 Build APK 试试:
五、运行
到目前为止,Launcher 的 APK 文件我们已经可以编译出来了,不妨试试?push 到你的设备里面,你会发现 Launcher 黑屏,是起不来的。
别慌,我们继续解决报错。
5.1 报错 1
首先,你可能会看到以下报错:
09-13 08:05:14.899 8505 8529 E AndroidRuntime: FATAL EXCEPTION: UiThreadHelper
09-13 08:05:14.899 8505 8529 E AndroidRuntime: Process: com.android.launcher3, PID: 8505
09-13 08:05:14.899 8505 8529 E AndroidRuntime: java.lang.IllegalAccessError: Method 'void com.android.launcher3.views.ActivityContext.lambda$hideKeyboard$2(android.view.inputmethod.InputMethodManager, android.os.IBinder)' is inaccessible to class 'com.android.launcher3.views.ActivityContext$$ExternalSyntheticLambda1' (declaration of 'com.android.launcher3.views.ActivityContext$$ExternalSyntheticLambda1' appears in /data/app/~~cpByXOaM5sPPePJ4cFVhKQ==/com.android.launcher3-3K34Q-5lZ3pccyDjykl43Q==/base.apk!classes14.dex)
09-13 08:05:14.899 8505 8529 E AndroidRuntime: at com.android.launcher3.views.ActivityContext$$ExternalSyntheticLambda1.run(Unknown Source:6)
09-13 08:05:14.899 8505 8529 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:958)
09-13 08:05:14.899 8505 8529 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)
09-13 08:05:14.899 8505 8529 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:205)
09-13 08:05:14.899 8505 8529 E AndroidRuntime: at android.os.Looper.loop(Looper.java:294)
09-13 08:05:14.899 8505 8529 E AndroidRuntime: at android.os.HandlerThread.run(HandlerThread.java:67)
解决方法:
📄 定位到:ActivityContext.java// 注释掉这部分
// if (imm != null && token != null) {
// UI_HELPER_EXECUTOR.execute(() -> {
// if (imm.hideSoftInputFromWindow(token, 0)) {
// // log keyboard close event only when keyboard is actually closed
// MAIN_EXECUTOR.execute(() ->
// getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED));
// }
// });
// }
5.2 报错 2
再次运行,报错信息如下:
09-13 08:09:58.574 21935 21962 E AndroidRuntime: FATAL EXCEPTION: launcher-loader
09-13 08:09:58.574 21935 21962 E AndroidRuntime: Process: com.android.launcher3, PID: 21935
09-13 08:09:58.574 21935 21962 E AndroidRuntime: java.lang.IllegalAccessError: Method 'int com.android.launcher3.util.FlagOp.lambda$removeFlag$2(int, int)' is inaccessible to class 'com.android.launcher3.util.FlagOp$$ExternalSyntheticLambda2' (declaration of 'com.android.launcher3.util.FlagOp$$ExternalSyntheticLambda2' appears in /data/app/~~smscPfmSXdORUMxSIVSXWA==/com.android.launcher3-htSGu13r7cEsjDxCWxbHeA==/base.apk!classes3.dex)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.util.FlagOp$$ExternalSyntheticLambda2.apply(Unknown Source:4)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.icons.BitmapInfo.withFlags(BitmapInfo.java:85)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.icons.cache.BaseIconCache.getDefaultIcon(BaseIconCache.java:378)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.icons.cache.BaseIconCache.isDefaultIcon(BaseIconCache.java:397)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.icons.IconCache.loadIconSubsection(IconCache.java:456)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.icons.IconCache.lambda$getTitlesAndIconsInBulk$16(IconCache.java:404)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.icons.IconCache.$r8$lambda$5-QJc-TjC50rjAxnltzqbHffkpc(Unknown Source:0)
09-13 08:09:58.574 21935 21962 E AndroidRuntime: at com.android.launcher3.icons.IconCache$$ExternalSyntheticLambda13.accept(Unknown Source:6)
解决方法:定位到 iconloaderlib/src/com/android/launcher3/util/FlagOp.java,如下修改:
修改后如下:
/*** Returns a new OP which adds the provided flag after applying all previous operations*/default FlagOp addFlag(int flag) {//return i -> apply(i) | flag;return new FlagOp() {@Overridepublic int apply(int i) {return FlagOp.this.apply(i) | flag;}};}/*** Returns a new OP which removes the provided flag after applying all previous operations*/default FlagOp removeFlag(int flag) {//return i -> apply(i) & ~flag;return new FlagOp() {@Overridepublic int apply(int i) {return FlagOp.this.apply(i) & ~flag;}};}
至此,恭喜你,你的 Launcher3 源码已经成功转型为 Gradle 编译,并且可以完美起来,看个图吧: