Android Studio 如何更便捷开发系统 App

2,057 阅读3分钟

System App

最近接触系统App相关的开发,刚开始得知在系统源码中,开发系统应用,As 引用库的时候,居然不能代码联想,布局也不能预览,实在不习惯。后面搜了下网上的资源,有一些介绍,也不是特别完整,于是自己把这些零碎的点,整理出来,方面后续自己看看。

本文主要解决以下几个问题:

  1. 代码移植、资源移植
  2. 系统隐藏代码未找到
  3. aidl 代码问题
  4. protobuf 代码问题
  5. lint 警告提示

代码移植、资源移植

这一步,就是把系统App的代码拷贝出来(例如:/packages/apps/Settings),相当于移植到一个新的项目。创建一个新的 As 工程,然后按照源码的目录层级创建,记得包名跟源码一致,尽可能保存目录层级一致,接着就是各种 copy 操作了,把 src、res 等目录都搬过去新项目中。在移植的过程,需要将 Android.bp 文件里面依赖的库,按照 gradle 的方式来依赖进去。例如:

 static_libs: [
        "com.google.android.material_material",
        "androidx.transition_transition",
        "androidx-constraintlayout_constraintlayout",
        "androidx.core_core",
        "androidx.media_media",
        "androidx.legacy_legacy-support-core-utils",
        "androidx.legacy_legacy-support-core-ui",
        "androidx.fragment_fragment",
        "androidx.appcompat_appcompat",
        "androidx.preference_preference",
        "androidx.recyclerview_recyclerview",
        "androidx.legacy_legacy-preference-v14",
        "androidx.leanback_leanback",
        "androidx.leanback_leanback-preference",
        "androidx.lifecycle_lifecycle-extensions",
        "androidx.lifecycle_lifecycle-common-java8",
        "kotlin-stdlib",
        "kotlinx-coroutines-android",
        "androidx.navigation_navigation-common-ktx",
        "androidx.navigation_navigation-fragment-ktx",
        "androidx.navigation_navigation-runtime-ktx",
        "androidx.navigation_navigation-ui-ktx",
        ]

对应到 gradle 代码,这个过程十分麻烦,因为很多资源缺失,需要一个个的寻找,以及代码的移植还会关联其他工程代码。而且库的版本也是需要注意的。所以需要耐心解决

    compileOnly files('libs/framework.jar')
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'

    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'com.android.support:multidex:1.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'

    implementation "com.google.protobuf:protobuf-javalite:3.13.0"

    def nav_version = "2.3.5"

    // Java language implementation
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
    // Feature module Support
    implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"

    def lifecycle_version = "2.3.1"
    def arch_version = "2.1.0"

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    // Lifecycles only (without ViewModel or LiveData)
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

    // ???
    implementation "android.arch.lifecycle:extensions:1.1.1"

    // Saved state module for ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

    // Annotation processor
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
    // alternately - if using Java8, use the following instead of lifecycle-compiler
    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

    // optional - helpers for implementing LifecycleOwner in a Service
    implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"

    // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
    implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

    // optional - ReactiveStreams support for LiveData
    implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"

    // optional - Test helpers for LiveData
    testImplementation "androidx.arch.core:core-testing:$arch_version"

    def leanback_version = "1.2.0-alpha01"

    implementation "androidx.leanback:leanback:$leanback_version"

    // leanback-preference is an add-on that provides a settings UI for TV apps.
    implementation "androidx.leanback:leanback-preference:$leanback_version"

    // leanback-paging is an add-on that simplifies adding paging support to a RecyclerView Adapter.
    implementation "androidx.leanback:leanback-paging:1.1.0-alpha08"

    // leanback-tab is an add-on that provides customized TabLayout to be used as the top navigation bar.
    implementation "androidx.leanback:leanback-tab:1.1.0-beta01"

如果项目源码存在多个 src 目录,需要在 gradle 中指定 java 目录

    sourceSets {
        main {
            java.srcDirs = ['src/main/src', 'src/main/src2', 'src/main/src_gen']
            // 定义proto文件目录
            proto {
//                srcDir 'src/main/java'
                srcDir  'src/main/src'
                include '**/*.proto'
            }
        }
    }

系统隐藏代码未找到

系统源码编译之后,找到/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates 目录下的 classes.jar 文件,更名为 framework.jar ,按照jar包的引用方式,依赖进去工程。

同时需要更改jar的加载顺序,在工程目录的 gradle 添加如下代码

allprojects {
    repositories {
        google()
        jcenter()
    }
    gradle.projectsEvaluated {
        tasks.withType(JavaCompile) {

            options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"

//            options.compilerArgs.add('-Xbootclasspath/p:app/libs/framework.jar')
            Set<File> fileSet = options.bootstrapClasspath.getFiles()
            List<File> newFileList =  new ArrayList<>();
            //"../framework.jar" 为相对位置,需要参照着修改,或者用绝对位置
            // 我这里用的是绝对路径,注意区分 linux 系统与 window 系统的反斜杠
            newFileList.add(new File("/xxx/framework.jar"))
            newFileList.addAll(fileSet)
            options.bootstrapClasspath = files(newFileList.toArray())
//            options.bootstrapClasspath.getFiles().forEach(new Consumer<File>() {
//                @Override
//                void accept(File file) {
//                    println(file.name)
//                }
//            })
            //options.compilerArgs.add('-Xbootclasspath/p:app\\libs\\framework.jar')
        }
    }
}

aidl 代码问题

aidl 代码可以用两种方式处理,一种是直接拷贝aidl的生成物,本质还是java代码,另一种方式是按源码那样创建aidl文件

protobuf 代码问题

需要正确引入对应的 protobuf 的版本,以及生成代码的目录,我记得我当时还因为版本不匹配导致一些错误,具体时间太久了,当时也没存记录。

plugins {
    id 'com.android.application'
    id 'com.google.protobuf'
    id 'kotlin-android'
    id 'kotlin-kapt'
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.13.0"
    }
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                java {
                    option "lite"
                }
            }
        }
    }
}

···

// 工程 gradle 配置
buildscript {
    ext.kotlin_version = "1.4.32"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.1.3"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.13"
    }
}

lint 警告提示

源码中会使用一些过时的方法,在打包过程会导致失败。需要在 gradle 中配置,错误不中断

 lintOptions {
        abortOnError false
    }

小结

整个过程,就是多次修改,拷贝,然后编译的过程,直到没有错误提示,能够成功生成apk的时候,就成功了。之后就可以愉快的关联代码,以及布局预览了。