美文网首页
Jetpack太香了,系统App也想用,怎么办?

Jetpack太香了,系统App也想用,怎么办?

作者: 今日Android | 来源:发表于2021-06-24 10:04 被阅读0次

    第三方App使用Jetpack等开源框架非常流行,在Gradle文件简单指定即可。然而ROM内置的系统App在源码环境下进行开发,与第三方App脱节严重,采用开源框架的情况并不常见。但如果系统App也集成了Jetpack或第三方框架,开发效率则会大大提高。

    前言

    系统App开发者,很少采用Jetpack 以及第三方框架的原因主要有几点:

    1. 导入麻烦:有的框架过于庞大,可能依赖的库比较多,编译文件的构建比较繁琐,没有gradle那么智能

    2. 功能单一:系统App注重功能性,业务逻辑较少,依赖庞大库文件的场景不多

    3. 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(就按照这个文件配置就可以了)

    2rVokj.png

    如何确定源码中哪些jetpack 库可以使用呢?

    在Android.bp 中搜索,或者看androidx目录下包含了什么

    prebuilts/sdk/current/androidx/m2repository/androidx$ ls

    image

    导入第三方的开源框架

    以上讲的是引入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

    image

    好吧,看到上图我傻眼了,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引入失败,但是整个过程我觉得有必要分享一下,给大家一些导入新框架的参考。

    源码环境里集成开源框架的流程

    2xipH1.png

    常用开源框架的对照表

    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目录下配置,项目中使用,也可以配置在项目中,都是可以的。

    本文在开源项目:https://github.com/Android-Alvin/Android-LearningNotes 中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中...

    相关文章

      网友评论

          本文标题:Jetpack太香了,系统App也想用,怎么办?

          本文链接:https://www.haomeiwen.com/subject/abdwyltx.html