作者:Jingle_zhang
第三方App使用
Jetpack
等开源框架非常流行,在Gradle文件简单指定即可。然而ROM内置的系统App在源码环境下进行开发,与第三方App脱节严重,采用开源框架的情况并不常见。但如果系统App也集成了Jetpack或第三方框架,开发效率则会大大提高。
前言
系统App开发者,很少采用Jetpack 以及第三方框架的原因主要有几点:
-
导入麻烦:有的框架过于庞大,可能依赖的库比较多,编译文件的构建比较繁琐,没有gradle那么智能
-
功能单一:系统App注重功能性,业务逻辑较少,依赖庞大库文件的场景不多
-
license风险:引用第三方框架的话,需要特别声明license ,会尽量避免采用
但对于功能复杂,架构庞大的系统App而言,集成第三方框架显得尤为必要。比如Android系统里最核心的App SystemUI,就采用了知名的DI框架Dagger2 。Dagger2的引入使得功能庞杂的SystemUI管理各个依赖模块变得游刃有余。
SystemUI将Dagger2集成的方式给了我启发,探索和总结了Android 源码中如何配置Jetpack 以及第三方库,希望能够帮到大家。
源码编译说明
与Gradle不同,源码环境里的编译构建都是配置在.mk或者.bp文件里的,配置起来较为繁琐。
.bp文件::Android.bp是用来替换Android.mk的配置文件,它使用Blueprint框架来解析。Blueprint是生成、解析Android.bp的工具,是Soong的一部分。Soong则是专为Android编译而设计的工具,Blueprint只是解析文件的形式,而Soong则解释内容的含义,最终转换成Ninja文件。下文bp 就是指.bp的文件
**注意:**以下基于Android 11上进行的演示,Android 10及之前部分Jetpack框架没有集成进源码,需留意
gradle切换到bp
gradle和bp的对比
看一个使用aar和注解库的例子。
看一个AndroidStudio(以下简称AS)下build.gradle 文件里包的导入代码:
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
//room
def room_version = "2.3.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
}
ROM环境里的编译依赖.bp 配置如下:
android_app {
......
static_libs: [ "androidx.appcompat_appcompat", "com.google.android.material_material", "androidx-constraintlayout_constraintlayout", "androidx.room_room-runtime", ],
plugins: ["androidx.room_room-compiler-plugin"],
......
}
导入关键字的差异
依赖文件里的导入关键字:
在AS和AOSP里面导入包的关键字有些差异,又分为两种情况。
build.gradle | .bp | |
---|---|---|
代码库 | implementation | static_libs |
注解使用的库 | annotationProcessor | plugins |
引入库文件(libs):比较常见。引入的方式有多种。下文会讲具体的几种方式。
引入注解库:比较流行,源码中使用比较繁琐,下文会重点讲解。
库文件的导入规则
眼尖的同学已经看出规律了
如:implementation ‘androidx.appcompat:appcompat:1.2.0’
bp 文件中:androidx.appcompat_appcompat,将“:” 改为 “”即可,不需要加版本号。其实就是group 与 name 中间用“”连接,基本上符合上述规则,当然也有特殊
注解库的导入规则
如今框架流行注解编程。
gradle 配置:annotationProcessor “androidx.room:room-compiler:$room_version”
bp 中就需要使用到plugins,对应配置plugins: [“androidx.room_room-compiler-plugin”]
根据jar 包的规则,那plugin 命名应该是“:” 改为 ”_" version+“-plugin” 。
SystemUI 使用Dagger2配置 plugins: [“dagger2-compiler-2.19”],所以命名规则并不是上文猜测的那样。
那如何确定Jetpack框架的名称呢?
确定Jetpack框架的名称
源码编译,所有的内容和都在源码中,都需要在源码环境中寻找。
以Room 为例
在prebuilts/sdk/current/androidx/Android.bp 配置了引入jar包 中有如下配置
android_library {
name: "androidx.room_room-runtime",//名称
......
manifest: "manifests/androidx.room_room-runtime/AndroidManifest.xml",//配置manifast
static_libs: [//两个room库文件,三个依赖的库文件
"androidx.room_room-runtime-nodeps",
"androidx.room_room-common",
"androidx.sqlite_sqlite-framework",
"androidx.sqlite_sqlite",
"androidx.arch.core_core-runtime",
],
}
插件配置在prebuilts/sdk/current/androidx/JavaPlugins.bp
java_plugin {
name: "androidx.room_room-compiler-plugin",//名称
static_libs: [//1个room库文件,1个依赖的库文件
"androidx.room_room-compiler",
"kotlin-reflect"
],
processor_class: "androidx.room.RoomProcessor",//需要指定处理的类
}
注意:AS 开发 并不需要配置 “processor_class”,我反编译了room-compiler,找到了RoomProcessor.java.(AS 为什么不需要指定,我这里我就不研究了)
看下图,META-INF/services/javax.annotation.processing.Processor 文件中配置了RoomProcessor.java(就按照这个文件配置就可以了)
如何确定源码中哪些jetpack 库可以使用呢?
在Android.bp 中搜索,或者看androidx目录下包含了什么
prebuilts/sdk/current/androidx/m2repository/androidx$ ls
导入第三方的开源框架
以上讲的是引入Jetpack相关jar包,其他常见的是否包含呢?如Glide,它是不属于androidx 的
第三方库,Android 源码中整理就不算好了,使用比较乱。下面我梳理下
导入下载的jar包
大家最常用的,把 jar 包 放到 libs,就可以了(当然,比较简单,与其他库关联较少可以采用此种方式)
java_import {
name: "disklrucache-jar",
jars: ["disklrucache-4.12.0.jar"],
sdk_version: "current",
}
android_library_import {
name: "glide-4.12.0",
aars: ["glide-4.12.0.aar"],
sdk_version: "current",
}
android_library_import {
name: "gifdecoder-4.12.0",
aars: ["gifdecoder-4.12.0.aar",],
sdk_version: "current",
}
android_library_import {
name: "exifinterface-1.2.0",
aars: ["exifinterface-1.2.0.aar",],
sdk_version: "current",
}
android_app {
......
static_libs: [
"disklrucache-jar",
"glide-4.12.0",
"gifdecoder-4.12.0",
"exifinterface-1.2.0"
],
}
导入AOSP内置的jar包
常用第三方放在了prebuilts/tools/common/m2/repository/下面包含了很多库文件,如Glide,Okhttp,但比较尴尬的是,.bp文件并没有写好。应用需要自己编写,编写方式可以参考上文。
以后google应该会把 external 下 的整合到这个里面,可以关注下prebuilts/tools/common/m2/repository 中Android.bp文件的变化。
如:prebuilts/maven_repo/bumptech/Android.bp
java_import {
name: "glide-prebuilt",
jars: [
"com/github/bumptech/glide/glide/4.8.0/glide-4.8.0.jar",
"com/github/bumptech/glide/disklrucache/4.8.0/disklrucache-4.8.0.jar",
"com/github/bumptech/glide/gifdecoder/4.8.0/gifdecoder-4.8.0.jar",
],
jetifier: true,
notice: "LICENSE",
}
Android.bp 直接用"glide"了
static_libs: [
"glide-prebuilt"
],
导入jar包源码
external 下面 很多第三方库的源码,如Glide的源码,目录为external/glide/
android_library {
name: "glide",
srcs: [
"library/src/**/*.java",
"third_party/disklrucache/src/**/*.java",
"third_party/gif_decoder/src/**/*.java",
"third_party/gif_encoder/src/**/*.java",
],
manifest: "library/src/main/AndroidManifest.xml",
libs: [
"android-support-core-ui",
"android-support-compat",
"volley",
],
static_libs: [
"android-support-fragment",
],
sdk_version: "current",
}
复制代码
App 的Android.bp 直接用"glide"了
static_libs: [
"glide"
],
以上三种方式都是引入 Android 中源码存在的。不存在怎么办,Android源码 不像 AS,连上网,配置下版本号就可以下载。
内置新的Jetpack框架
引入第三方库文件方式,方式一:aar包导入。就可以。但这里不讨论,找些复杂的,包含annotationProcessor(bp 中的plugin) 。Hilt 是 Google 相对较新的框架。
Hilt基于Dagger2开发,又针对Android进行了专属的DI优化。
所以在导入Dagger2和它的依赖文件之外还需要导入Hilt专属的一堆库和依赖文件。
1. 获取框架的库文件
一般来说AS里导入完毕的目录下即可获取到对应的库文件,路径一般在 :C:\Users\xxx.gradle\caches\modules-2\files-2.1\com.google.dagger\hilt-android
2. 确定额外的依赖文件
为什么需要额外的依赖文件?
完全依赖AS开发可能不知道,导入的包的同时可能引入其他的包。
如Hilt的是在dagger2基础上开发,当然会引入Dagger2,
使用注解,需要javax.annotation包。
Dagger2,javax.annotation 在Gradle 自动下载好的,非项目中明确配置的,我们称之为依赖包。
使用Gradle 自动下载,都会有pom 文件。“dependency”,表示需要依赖的jar 包,还包含了版本号等
如:hilt-android-2.28-alpha.pom
`<dependency>`
`<groupId>com.google.dagger</groupId>`
`<artifactId>dagger</artifactId>` //依赖的dagger2
`<version>2.28</version>`//dagger2的版本
`</dependency>`
`<dependency>`
`<groupId>com.google.dagger</groupId>`
`<artifactId>dagger-lint-aar</artifactId>`
`<version>2.28</version>`
`</dependency>`
`<dependency>`
`<groupId>com.google.code.findbugs</groupId>`
`<artifactId>jsr305</artifactId>`
`<version>3.0.1</version>`
`</dependency>`
......
3. 导入需要的依赖文件
比如SystemUI,已经导入了一些文件,只要导入剩余的文件即可。
一般常用的 源码中都是存在的,决定copy 之前,可以看下先源码中是否存在,存在可以考虑使用。
当然也有例外,如Hilt 我依赖的是源码中dagger2是2.19 版本,编译中报错,没有找到dagger2 中的class,反编译jar确实不存在,使用2.28 的dagger 版本,问题就解决了。所以说可能存在库文件版本较老的情况。
以下就是新增的文件夹,其中manifests 后文中有讲。
manifests/
repository/com/google/dagger/dagger-compiler/2.28/
repository/com/google/dagger/dagger-producers/2.28/
repository/com/google/dagger/dagger-spi/2.28/
repository/com/google/dagger/dagger/2.28/
repository/com/google/dagger/hilt-android-compiler/
repository/com/google/dagger/hilt-android/
4. 编写最终的bp文件
这一步就是把依赖的包,关联起来,根据上文的 pom 文件。
- 配置dagger2 2.28 的jar
java_import {
name: "dagger2-2.28",
jars: ["repository/com/google/dagger/dagger/2.28/dagger-2.28.jar"],
host_supported: true,
}
- 配置 dagger2-compiler 2.28 的jar (annotationProcessor 依赖的jar包)
java_import_host {
name: "dagger2-compiler-2.28-import",
jars: [
"repository/com/google/dagger/dagger-compiler/2.28/dagger-compiler-2.28.jar",
"repository/com/google/dagger/dagger-producers/2.28/dagger-producers-2.28.jar",
"repository/com/google/dagger/dagger-spi/2.28/dagger-spi-2.28.jar",
"repository/com/google/dagger/dagger/2.28/dagger-2.28.jar",
"repository/com/google/guava/guava/25.1-jre/guava-25.1-jre.jar",
"repository/com/squareup/javapoet/1.11.1/javapoet-1.11.1.jar",
"repository/com/google/dagger/dagger-google-java-format/1.6/google-java-format-1.6-all-deps.jar",
],
}
- 配置dagger2 的 plugin (annotationProcessor)
java_plugin {
name: "dagger2-compiler-2.28",
static_libs: [
"dagger2-compiler-2.28-import",
"jsr330",
],
processor_class: "dagger.internal.codegen.ComponentProcessor",
generates_api: true,
}
- 配置 hilt 依赖的aar包
android_library_import {
name: "hilt-2.82-nodeps",
aars: ["repository/com/google/dagger/hilt-android/2.28-alpha/hilt-android-2.28-alpha.aar"],
sdk_version: "current",
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
min_sdk_version: "14",
static_libs: [
"dagger2-2.28",
"jsr305",
"androidx.activity_activity",
"androidx.annotation_annotation",
"androidx.fragment_fragment",
],
}
-
配置hilt 的包
android_library 表示 aar 包,所以必须要配置manifests ,在上文中多出的manifasts文件夹中 放的就是这个文件,AndroidManifest.xml来自hilt-android-2.28-alpha.aar 中
android_library {
name: "hilt-2.82",
manifest: "manifests/dagger.hilt.android/AndroidManifest.xml",
static_libs: [
"hilt-2.82-nodeps",
"dagger2-2.28"
],
......
}
- 配置 hilt-compiler 2.82 jar包
java_import_host {
name: "hilt-compiler-2.82-import",
jars: [
"repository/com/google/dagger/dagger-compiler/2.28/dagger-compiler-2.28.jar",
"repository/com/google/dagger/dagger-producers/2.28/dagger-producers-2.28.jar",
"repository/com/google/dagger/dagger-spi/2.28/dagger-spi-2.28.jar",
"repository/com/google/dagger/dagger/2.28/dagger-2.28.jar",
"repository/com/google/guava/guava/25.1-jre/guava-25.1-jre.jar",
"repository/com/squareup/javapoet/1.11.1/javapoet-1.11.1.jar",
"repository/com/google/dagger/dagger-google-java-format/1.6/google-java-format-1.6-all-deps.jar",
"repository/com/google/dagger/hilt-android-compiler/2.28-alpha/hilt-android-compiler-2.28-alpha.jar",
"repository/javax/inject/javax.inject/1/javax.inject-1.jar"
],
}
-
配置hilt的 plugin (annotationProcessor)
反编译查看需要配置的Processer
好吧,看到上图我傻眼了,11个。下文代码我只贴了一个,需要写11个,其他省略。
java_plugin {
name: "hilt-compiler-2.82",
static_libs: [
"hilt-compiler-2.82-import",
"jsr330",
],
processor_class: "dagger.hilt.processor.internal.root.RootProcessor",
generates_api: true,
}
- 项目中引用
`static_libs: [`
`"androidx-constraintlayout_constraintlayout",`
`"androidx.appcompat_appcompat",`
`"com.google.android.material_material",`
`"androidx.room_room-runtime",`
`"androidx.lifecycle_lifecycle-viewmodel",`
`"androidx.lifecycle_lifecycle-livedata",`
`"hilt-2.82",`
`"jsr330"`
`],`
`plugins: ["androidx.room_room-compiler-plugin",`
`"hilt-compiler-2.82",`
`"hilt-compiler-2.82-UninstallModulesProcessor",`
`"hilt-compiler-2.82-TestRootProcessor",`
`"hilt-compiler-2.82-DefineComponentProcessor",`
`"hilt-compiler-2.82-BindValueProcessor",`
`"hilt-compiler-2.82-CustomTestApplicationProcessor",`
`"hilt-compiler-2.82-AndroidEntryPointProcessor",`
`"hilt-compiler-2.82-AggregatedDepsProcessor",`
`"hilt-compiler-2.82-OriginatingElementProcessor",`
`"hilt-compiler-2.82-AliasOfProcessor",`
`"hilt-compiler-2.82-GeneratesRootInputProcessor",`
`],`
-
编译确认
编译失败了!看到报错,我的心也凉了。需要配置Gradle 插件。bp 可以配置Gradle插件?
看了下com/google/dagger/hilt-android-gradle-plugin/,但是并不清楚bp 怎么配置,在源码里,只知道一处:prebuilts/gradle-plugin/Android.bp,但并没有尝试成功。有兴趣的同学,可以研究下。
而且hilt-android-gradle-plugin 的jar包,依赖包 至少十几个。
public class MainActivity extends AppCompatActivity { ^ Expected @AndroidEntryPoint to have a value. Did you forget to apply the Gradle Plugin? [Hilt] Processing did not complete. See error above for details.
public class MainFragment extends BaseFragment { ^ Expected @AndroidEntryPoint to have a value. Did you forget to apply the Gradle Plugin? [Hilt] Processing did not complete. See error above for details.
public class AppApplication extends Application { ^ Expected @HiltAndroidApp to have a value. Did you forget to apply the Gradle Plugin? [Hilt] Processing did not complete. See error above for details.
虽然Hilt引入失败,但是整个过程我觉得有必要分享一下,给大家一些导入新框架的参考。
源码环境里集成开源框架的流程
常用开源框架的对照表
build.gradle | Android.bp | AOSP源码位置 |
---|---|---|
androidx.appcompat:appcompat | androidx.appcompat_appcompat | /sdk/current/androidx/Android.bp |
androidx.core:core | androidx.core_core | prebuilts/sdk/current/androidx/Android.bp |
com.google.android.material:material | com.google.android.material_material | prebuilts/sdk/current/extras/material-design-x/Android.bp |
androidx.constraintlayout:constraintlayout | androidx-constraintlayout_constraintlayout | prebuilts/sdk/current/extras/constraint-layout-x/Android.bp |
androidx.lifecycle:lifecycle-livedata | androidx.lifecycle_lifecycle-livedata | prebuilts/sdk/current/androidx/Android.bp |
androidx.lifecycle:lifecycle-viewmodel | androidx.lifecycle_lifecycle-viewmodel | prebuilts/sdk/current/androidx/Android.bp |
androidx.recyclerview:recyclerview | androidx.recyclerview_recyclerview | prebuilts/sdk/current/androidx/Android.bp |
androidx.annotation:annotation | androidx.annotation_annotation | prebuilts/sdk/current/androidx/Android.bp |
androidx.viewpager2:viewpager2 | androidx.viewpager2_viewpager2 | prebuilts/sdk/current/androidx/Android.bp |
androidx.room:room-runtime | androidx.room_room-runtime | prebuilts/sdk/current/androidx/Android.bp |
glide | glide-prebuilt | prebuilts/maven_repo/bumptech/Android.bp |
gson | gson-prebuilt-jar | prebuilts/tools/common/m2/Android.bp |
Robolectric相关 | Robolectric相关 | prebuilts/tools/common/m2/robolectric.bp |
经验总结
1、build.gradle 需要配置 额外插件的,如hilt、databinding viewbinding 不建议使用源码编译。
2、建议使用 AOSP 源码 中 bp 已经配置好的。这样就可以直接使用了。
3、jetpack 包引入或者androidx 引入,建议先prebuilts/sdk/current/androidx 下寻找配置好的bp 文件
4、非androidx ,建议先在prebuilts/tools/common/m2下寻找寻找配置好的bp 文件
5、文章中的例子都是prebuilts目录下配置,项目中使用,也可以配置在项目中,都是可以的。