区块链技术博客
www.b2bchain.cn

SystemUI 源码编译切换为 Android Studio 独立编译求职学习资料

本文介绍了SystemUI 源码编译切换为 Android Studio 独立编译求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

背景

当前作者的项目中Android APP只有SystemUI仍然需要跟随源码编译,主要是因为SystemUI没法单独在Android Studio编译通过,缺少的依赖太多了。所以只能通过编辑器写完代码后,合并到系统源码中单编出 APK 再验证,整个过程繁琐且耗时。如果有办法可以像开发普通 App 那样在 Android Studio 中开发系统 App,并且还能随时跟系统源码保持一致,相信能解决编译和调试难的问题。因此本文期望在本地Windows环境下对SystemUI进行二进制编译构建,同时代码开发和调试也在本地进行,更进一步,期望SystemUI工程不依赖Android版本,做到跨版本兼容。

目标

Android SystemUI 工程从源码构建方式切换到二进制独立构建编译,在Android 中修改代码编译验证后即可将代码push到服务器。

本文阅读对象

从事Android 项目开发,工程构建的研发工程师。本文虽然是基于Android9.0 源码进行说明,但其解耦隔离方案可以扩展到其他Android 工程(例如Settings)和其他Android版本(Android 10.0)。

前提条件

  1. Android 9.0 源码
  2. 完整编译过SystemUI工程
  3. 熟悉和了解Android gradle 编译APK的流程

创建Android Studio工程

创建一个新的工程

选择No Activity
包名:com.android.systemui

创建module

类型为Android Library,
需要创建四个module,为什么是这四个,可以看SystemUI的Android.mk和源码下的AndroidManifest.xml文件的个数,一个AndroidManifest.xml文件需要新建一个模块

模块 包名
keyguard com.android.keyguard
plugin com.android.systemui.plugins
shared com.android.systemui.shared
settingslib com.android.settingslib

删除不用的文件和资源

删除每个模块下的testandroidTest目录
删除gradle – dependencies下的所有内容

将每个模块的build.grale 中属性配置为当前API级别

compileSdkVersion 28 buildToolsVersion '28.0.3'

关闭AndroidX

android.useAndroidX=false android.enableJetifier=false

​ 至此,Android Studio工程结构如下

SystemUI 源码编译切换为 Android Studio 独立编译

小技巧:

由于过程较为复杂,建议将工程通过git进行管理,这样一旦有推进可以及时在本地提交,一旦有问题可以及时回退。

拷贝源码文件

此步骤需要根据SystemUI下的Android.mk文件进行详细分析,逐个分析上述中app工程和四个module的源码,和组件依赖。

SystemUI 源码编译切换为 Android Studio 独立编译

源文件 路径(相对于项目根目录) 说明
frameworks/base/packages/SystemUI SystemUI_SRC
frameworks/base/packages/SettingsLib SettingsLib_SRC F4
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar jar/framework.jar F3
out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/classes.jar jar/telephony-common.jar F2
out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar jar/core-libart.jar F4
out/target/common/obj/JAVA_LIBRARIES/core-oj_intermediates/classes.jar jar/core-oj.jar F4

下面这部分文件是aidl,proto,logtags文件生成的java文件,直接拷贝到新建的模块目录下。

源文件 路径(相对于项目根目录)
out/target/common/obj/JAVA_LIBRARIES/SystemUI-proto_intermediates/proto/src/* app/src/main/java
out/target/common/obj/JAVA_LIBRARIES/SystemUI-tags_intermediates/logtags/src/* app/src/main/java
out/target/common/obj/APPS/SystemUI_intermediates/aidl/src/* app/src/main/java
out/target/common/obj/JAVA_LIBRARIES/SystemUISharedLib_intermediates/aidl/src/* shared/src/main/java
***/corejavaandroidhardwarefingerprint app/src/main/java

由于SystemUI的手机和车机共用一个工程,只不过存在资源差异,这部分是通过Overlay机制来实现的。如果有同学需要基于车机项目编译,那么需要将overlay资源也配置。至于资源路径,我们可以从编译脚本分析得到

Android Car的编译过程分析,选择car的版本,我们跟踪执行的脚本如下

build/envsetup.sh devicegenericcarcommon\car.mk packages/services/Car/car_product/build/car.mk packages/services/Car/car_product/build/car_base.mk PRODUCT_PACKAGE_OVERLAYS += packages/services/Car/car_product/overlay

从这里我们可以看出,Overlay的资源在packages/services/Car/car_product/overlay 目录下,这里我们将SettingsLib 和SystemUI下面的资源拷贝到工程目录下

源文件 路径(相对于项目根目录)
packagesservicesCarcar_productoverlayframeworksbasepackagesSettingsLib overlay/SettingsLib
packagesservicesCarcar_productoverlayframeworksbasepackagesSystemUI overlay/SystemUI

至此项目目录结构如下

SystemUI 源码编译切换为 Android Studio 独立编译

配置项目

  1. 项目工程build.gradle 文件配置
buildscript {     repositories {         maven { url 'https://maven.aliyun.com/repository/public' } //加快下载         google()     }     dependencies {         classpath "com.android.tools.build:gradle:4.1.1"     }  }  allprojects {     repositories {         maven { url 'https://maven.aliyun.com/repository/public' } //加快下载         google()     }      gradle.projectsEvaluated {         tasks.withType(JavaCompile) {             // 如果有多个jar需要提升优先级,用分号隔开,jar/core-oj.jar             options.compilerArgs.add('-Xbootclasspath/p:jar/framework.jar;')         }     } }
  1. app模块的gradle build.gradle配置
plugins {     id 'com.android.application' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         applicationId "com.android.systemui"         minSdkVersion 28         targetSdkVersion 28         versionCode 2         versionName "2.0"     }      signingConfigs {          main {             storeFile file("../keystore/platform.jks") //签名文件路径             storePassword "XXX"             keyAlias "XXX"             keyPassword "XXX"         }     }      buildTypes {         release {             minifyEnabled false             signingConfig signingConfigs.main //添加这一行             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }          debug {             minifyEnabled false             signingConfig signingConfigs.main //添加这一行             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }      }      compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main {             res.srcDirs = [                     '../SystemUI_SRC/res',                     '../overlay/SystemUI/res',                     '../SystemUI_SRC/res-keyguard',                     '../overlay/SystemUI/res-keyguard',             ]              java.srcDirs = [                     'src/main/java',                     '../SystemUI_SRC/src',             ]              manifest.srcFile '../SystemUI_SRC/AndroidManifest.xml'         }     }      lintOptions {         abortOnError false         checkReleaseBuilds false     }      aaptOptions {             additionalParameters '--auto-add-overlay'     }  }  dependencies {      compileOnly files('../jar/framework.jar')     compileOnly files('../jar/core-oj.jar')     compileOnly files('../jar/telephony-common.jar')     compileOnly files('../jar/core-libart.jar')     compileOnly files('../jar/android.car.jar')     compileOnly files('../jar/bluetoothkit.jar')     //需要那些依赖,可以在Android.mk里看到     //noinspection GradleCompatible     implementation 'com.android.support:support-v4:28.0.0'     implementation 'com.android.support:car:28.0.0-alpha5'     implementation 'com.android.support:appcompat-v7:28.0.0'     implementation 'com.android.support:recyclerview-v7:28.0.0'     implementation 'com.android.support:preference-v7:28.0.0'     implementation 'com.android.support:mediarouter-v7:28.0.0'     implementation 'com.android.support:palette-v7:28.0.0'     implementation 'com.android.support:preference-v14:28.0.0'     implementation 'com.android.support:leanback-v17:28.0.0'     implementation 'com.android.support:slices-builders:28.0.0'     implementation 'com.android.support:slices-core:28.0.0'     implementation 'com.android.support:slices-view:28.0.0'     implementation 'com.google.protobuf:protobuf-java:3.12.2'     implementation group: 'com.google.protobuf.nano', name: 'protobuf-javanano', version: '3.1.0'     //把几个模块包含进来     implementation project(path: ':settingslib')     implementation project(path: ':plugin')     implementation project(path: ':shared')     implementation project(path: ':keyguard') }
  1. keyguard 的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false            //add  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }  }  dependencies {  }
  1. plugin的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false            // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             java.srcDirs = [                     '../SystemUI_SRC/plugin/src',             ]         }     }  }  dependencies {     compileOnly files('../jar/framework.jar') }
  1. settingslib的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             res.srcDirs = [                     '../SettingsLib_SRC/res',             ]              java.srcDirs = [                     '../SettingsLib_SRC/src',             ]         }     } }  dependencies {     compileOnly files('../jar/core-libart.jar')      //lifecyce-runtime 包含在appcompat中26.1中及以上     implementation 'com.android.support:appcompat-v7:28.0.0'     implementation 'com.android.support:recyclerview-v7:28.0.0'     implementation 'com.android.support:preference-v7:28.0.0'     implementation 'com.android.support:preference-v14:28.0.0'  }
  1. shared的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             java.srcDirs=[                     'src/main/java',                     '../SystemUI_SRC/shared/src',             ]         }     } }  dependencies {      compileOnly files('../jar/framework.jar') }

至此项目框架已经搭建完成,但是里编译成功仍然有所差距。

解决编译问题

这里把作者编译过程的出现的问题记录如下

问题1:string重复资源,value/string.xml中相同name的字符串存在多份(default,tablet,device),由于Android Studio不支持这种方式。

解决方案:

  1. 由于涉及到多国语言,手动删除效率低下,可以通过脚本,将重复的字符串删除,只保留default类型的字符串

  2. 如果APK需要同时编译差异化的APK版本,可以将重复的字符串提取出来,通过设置不同product来编译出不同的APK

删除脚本比较简单

   echo delete $1    find ./ -name "strings.xml" |xargs sed -i "/$1/d"

问题2: Error:Found item Attr/intent more than once time

解决方案:如果只是在 App 内部或者 Lib 内部,是可以使用同名属性的,处理方法为,在 resources 顶部定义公用的属性,在 declare-styleable 内进行引用,如:

<?xml version="1.0" encoding="utf-8"?>  <resources>  <attr name="common_bar" format="integer"/>  <declare-styleable name="LocalAttr1">  <attr name="common_bar"/>  </declare-styleable>  <declare-styleable name="LocalAttr2">  <attr name="common_bar"/>  </declare-styleable>  </resources>

注意 LocalAttr1 和 LocalAttr2 的 common_bar 不能带 format,否则会变为属性声明,导致冲突。LocalAttr1 和 LocalAttr2 里面 common_bar 的 format 为外部声明的 integer,不能具有不同的类型。在外部声明 common_bar 时可以一次性声明多种format,如:

<attr name="common_bar" format="integer|string"/>

问题3:百分号的引用问题

Multiple annotations found at this line:  error: Multiple substitutions specified in non-positional format; did you mean to add  the formatted="false" attribute?  error: Unexpected end tag string

解决方案:出现这个错误的原因主要是因为strings字串中包含百分号(%),主要将字符串声明为无需格式化

<string 标签上增加属性:formatted="false">

问题4:orientaion 名称与系统名称重复

解决方案:本地自定义名称定义不和系统名称相同,例如

<attr name="orientation1">     <enum name="horizontal" value="0" />     <enum name="vertical" value="1" /> </attr>

问题5:无法引用系统资源,保持如下

AAPT: error: resource android: dimen/rounded_corner_radius not found.  AAPT: error: resource android: dimen/rounded_corner_radius_bottom not found.  AAPT: error: resource android: dimen/rounded_corner_radius_top not found

解决方案:找到资源的值,直接硬编码

<dimen name="rounded_corner_radius>0dp</dimen>

问题6:

SystemUI_SRCsrccomandroidsystemuipluginsPluginManagerImpl.java:97: 错误: 找不到符号   Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);    符号:   方法 setUncaughtExceptionPreHandler(PluginManagerImpl.PluginExceptionHandler)    位置: 类 Thread

解决方案:

  1. 由于plugin插件,目前在手机侧和其他设备都没有引用,此处代码本身没有被用到,因此可以注释
  2. 如果不期望注释,可以引入对应的jar进来,该API位于
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java 代码被编译到 ojluni-phony 组件中 http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/Android.mk

源码过程记录

项目设置为私有,如果有需要请留言沟通。

https://gitee.com/big4panda/systemui

后续工作

  1. 减少jar的依赖,telephony-common 于framework.jar 存在重合,后续考虑将telephony-common用到的API合并到framework.jar中
  2. 由于作者从事的是车机上的systemUI,其功能相比手机简单,后续将用不到的功能和模块进行精简,做到各个子服务按需启停,达到APK瘦身高效的目的。

背景

当前作者的项目中Android APP只有SystemUI仍然需要跟随源码编译,主要是因为SystemUI没法单独在Android Studio编译通过,缺少的依赖太多了。所以只能通过编辑器写完代码后,合并到系统源码中单编出 APK 再验证,整个过程繁琐且耗时。如果有办法可以像开发普通 App 那样在 Android Studio 中开发系统 App,并且还能随时跟系统源码保持一致,相信能解决编译和调试难的问题。因此本文期望在本地Windows环境下对SystemUI进行二进制编译构建,同时代码开发和调试也在本地进行,更进一步,期望SystemUI工程不依赖Android版本,做到跨版本兼容。

目标

Android SystemUI 工程从源码构建方式切换到二进制独立构建编译,在Android 中修改代码编译验证后即可将代码push到服务器。

本文阅读对象

从事Android 项目开发,工程构建的研发工程师。本文虽然是基于Android9.0 源码进行说明,但其解耦隔离方案可以扩展到其他Android 工程(例如Settings)和其他Android版本(Android 10.0)。

前提条件

  1. Android 9.0 源码
  2. 完整编译过SystemUI工程
  3. 熟悉和了解Android gradle 编译APK的流程

创建Android Studio工程

创建一个新的工程

选择No Activity
包名:com.android.systemui

创建module

类型为Android Library,
需要创建四个module,为什么是这四个,可以看SystemUI的Android.mk和源码下的AndroidManifest.xml文件的个数,一个AndroidManifest.xml文件需要新建一个模块

模块 包名
keyguard com.android.keyguard
plugin com.android.systemui.plugins
shared com.android.systemui.shared
settingslib com.android.settingslib

删除不用的文件和资源

删除每个模块下的testandroidTest目录
删除gradle – dependencies下的所有内容

将每个模块的build.grale 中属性配置为当前API级别

compileSdkVersion 28 buildToolsVersion '28.0.3'

关闭AndroidX

android.useAndroidX=false android.enableJetifier=false

​ 至此,Android Studio工程结构如下

SystemUI 源码编译切换为 Android Studio 独立编译

小技巧:

由于过程较为复杂,建议将工程通过git进行管理,这样一旦有推进可以及时在本地提交,一旦有问题可以及时回退。

拷贝源码文件

此步骤需要根据SystemUI下的Android.mk文件进行详细分析,逐个分析上述中app工程和四个module的源码,和组件依赖。

SystemUI 源码编译切换为 Android Studio 独立编译

源文件 路径(相对于项目根目录) 说明
frameworks/base/packages/SystemUI SystemUI_SRC
frameworks/base/packages/SettingsLib SettingsLib_SRC F4
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar jar/framework.jar F3
out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/classes.jar jar/telephony-common.jar F2
out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar jar/core-libart.jar F4
out/target/common/obj/JAVA_LIBRARIES/core-oj_intermediates/classes.jar jar/core-oj.jar F4

下面这部分文件是aidl,proto,logtags文件生成的java文件,直接拷贝到新建的模块目录下。

源文件 路径(相对于项目根目录)
out/target/common/obj/JAVA_LIBRARIES/SystemUI-proto_intermediates/proto/src/* app/src/main/java
out/target/common/obj/JAVA_LIBRARIES/SystemUI-tags_intermediates/logtags/src/* app/src/main/java
out/target/common/obj/APPS/SystemUI_intermediates/aidl/src/* app/src/main/java
out/target/common/obj/JAVA_LIBRARIES/SystemUISharedLib_intermediates/aidl/src/* shared/src/main/java
***/corejavaandroidhardwarefingerprint app/src/main/java

由于SystemUI的手机和车机共用一个工程,只不过存在资源差异,这部分是通过Overlay机制来实现的。如果有同学需要基于车机项目编译,那么需要将overlay资源也配置。至于资源路径,我们可以从编译脚本分析得到

Android Car的编译过程分析,选择car的版本,我们跟踪执行的脚本如下

build/envsetup.sh devicegenericcarcommon\car.mk packages/services/Car/car_product/build/car.mk packages/services/Car/car_product/build/car_base.mk PRODUCT_PACKAGE_OVERLAYS += packages/services/Car/car_product/overlay

从这里我们可以看出,Overlay的资源在packages/services/Car/car_product/overlay 目录下,这里我们将SettingsLib 和SystemUI下面的资源拷贝到工程目录下

源文件 路径(相对于项目根目录)
packagesservicesCarcar_productoverlayframeworksbasepackagesSettingsLib overlay/SettingsLib
packagesservicesCarcar_productoverlayframeworksbasepackagesSystemUI overlay/SystemUI

至此项目目录结构如下

SystemUI 源码编译切换为 Android Studio 独立编译

配置项目

  1. 项目工程build.gradle 文件配置
buildscript {     repositories {         maven { url 'https://maven.aliyun.com/repository/public' } //加快下载         google()     }     dependencies {         classpath "com.android.tools.build:gradle:4.1.1"     }  }  allprojects {     repositories {         maven { url 'https://maven.aliyun.com/repository/public' } //加快下载         google()     }      gradle.projectsEvaluated {         tasks.withType(JavaCompile) {             // 如果有多个jar需要提升优先级,用分号隔开,jar/core-oj.jar             options.compilerArgs.add('-Xbootclasspath/p:jar/framework.jar;')         }     } }
  1. app模块的gradle build.gradle配置
plugins {     id 'com.android.application' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         applicationId "com.android.systemui"         minSdkVersion 28         targetSdkVersion 28         versionCode 2         versionName "2.0"     }      signingConfigs {          main {             storeFile file("../keystore/platform.jks") //签名文件路径             storePassword "XXX"             keyAlias "XXX"             keyPassword "XXX"         }     }      buildTypes {         release {             minifyEnabled false             signingConfig signingConfigs.main //添加这一行             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }          debug {             minifyEnabled false             signingConfig signingConfigs.main //添加这一行             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }      }      compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main {             res.srcDirs = [                     '../SystemUI_SRC/res',                     '../overlay/SystemUI/res',                     '../SystemUI_SRC/res-keyguard',                     '../overlay/SystemUI/res-keyguard',             ]              java.srcDirs = [                     'src/main/java',                     '../SystemUI_SRC/src',             ]              manifest.srcFile '../SystemUI_SRC/AndroidManifest.xml'         }     }      lintOptions {         abortOnError false         checkReleaseBuilds false     }      aaptOptions {             additionalParameters '--auto-add-overlay'     }  }  dependencies {      compileOnly files('../jar/framework.jar')     compileOnly files('../jar/core-oj.jar')     compileOnly files('../jar/telephony-common.jar')     compileOnly files('../jar/core-libart.jar')     compileOnly files('../jar/android.car.jar')     compileOnly files('../jar/bluetoothkit.jar')     //需要那些依赖,可以在Android.mk里看到     //noinspection GradleCompatible     implementation 'com.android.support:support-v4:28.0.0'     implementation 'com.android.support:car:28.0.0-alpha5'     implementation 'com.android.support:appcompat-v7:28.0.0'     implementation 'com.android.support:recyclerview-v7:28.0.0'     implementation 'com.android.support:preference-v7:28.0.0'     implementation 'com.android.support:mediarouter-v7:28.0.0'     implementation 'com.android.support:palette-v7:28.0.0'     implementation 'com.android.support:preference-v14:28.0.0'     implementation 'com.android.support:leanback-v17:28.0.0'     implementation 'com.android.support:slices-builders:28.0.0'     implementation 'com.android.support:slices-core:28.0.0'     implementation 'com.android.support:slices-view:28.0.0'     implementation 'com.google.protobuf:protobuf-java:3.12.2'     implementation group: 'com.google.protobuf.nano', name: 'protobuf-javanano', version: '3.1.0'     //把几个模块包含进来     implementation project(path: ':settingslib')     implementation project(path: ':plugin')     implementation project(path: ':shared')     implementation project(path: ':keyguard') }
  1. keyguard 的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false            //add  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }  }  dependencies {  }
  1. plugin的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false            // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             java.srcDirs = [                     '../SystemUI_SRC/plugin/src',             ]         }     }  }  dependencies {     compileOnly files('../jar/framework.jar') }
  1. settingslib的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             res.srcDirs = [                     '../SettingsLib_SRC/res',             ]              java.srcDirs = [                     '../SettingsLib_SRC/src',             ]         }     } }  dependencies {     compileOnly files('../jar/core-libart.jar')      //lifecyce-runtime 包含在appcompat中26.1中及以上     implementation 'com.android.support:appcompat-v7:28.0.0'     implementation 'com.android.support:recyclerview-v7:28.0.0'     implementation 'com.android.support:preference-v7:28.0.0'     implementation 'com.android.support:preference-v14:28.0.0'  }
  1. shared的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             java.srcDirs=[                     'src/main/java',                     '../SystemUI_SRC/shared/src',             ]         }     } }  dependencies {      compileOnly files('../jar/framework.jar') }

至此项目框架已经搭建完成,但是里编译成功仍然有所差距。

解决编译问题

这里把作者编译过程的出现的问题记录如下

问题1:string重复资源,value/string.xml中相同name的字符串存在多份(default,tablet,device),由于Android Studio不支持这种方式。

解决方案:

  1. 由于涉及到多国语言,手动删除效率低下,可以通过脚本,将重复的字符串删除,只保留default类型的字符串

  2. 如果APK需要同时编译差异化的APK版本,可以将重复的字符串提取出来,通过设置不同product来编译出不同的APK

删除脚本比较简单

   echo delete $1    find ./ -name "strings.xml" |xargs sed -i "/$1/d"

问题2: Error:Found item Attr/intent more than once time

解决方案:如果只是在 App 内部或者 Lib 内部,是可以使用同名属性的,处理方法为,在 resources 顶部定义公用的属性,在 declare-styleable 内进行引用,如:

<?xml version="1.0" encoding="utf-8"?>  <resources>  <attr name="common_bar" format="integer"/>  <declare-styleable name="LocalAttr1">  <attr name="common_bar"/>  </declare-styleable>  <declare-styleable name="LocalAttr2">  <attr name="common_bar"/>  </declare-styleable>  </resources>

注意 LocalAttr1 和 LocalAttr2 的 common_bar 不能带 format,否则会变为属性声明,导致冲突。LocalAttr1 和 LocalAttr2 里面 common_bar 的 format 为外部声明的 integer,不能具有不同的类型。在外部声明 common_bar 时可以一次性声明多种format,如:

<attr name="common_bar" format="integer|string"/>

问题3:百分号的引用问题

Multiple annotations found at this line:  error: Multiple substitutions specified in non-positional format; did you mean to add  the formatted="false" attribute?  error: Unexpected end tag string

解决方案:出现这个错误的原因主要是因为strings字串中包含百分号(%),主要将字符串声明为无需格式化

<string 标签上增加属性:formatted="false">

问题4:orientaion 名称与系统名称重复

解决方案:本地自定义名称定义不和系统名称相同,例如

<attr name="orientation1">     <enum name="horizontal" value="0" />     <enum name="vertical" value="1" /> </attr>

问题5:无法引用系统资源,保持如下

AAPT: error: resource android: dimen/rounded_corner_radius not found.  AAPT: error: resource android: dimen/rounded_corner_radius_bottom not found.  AAPT: error: resource android: dimen/rounded_corner_radius_top not found

解决方案:找到资源的值,直接硬编码

<dimen name="rounded_corner_radius>0dp</dimen>

问题6:

SystemUI_SRCsrccomandroidsystemuipluginsPluginManagerImpl.java:97: 错误: 找不到符号   Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);    符号:   方法 setUncaughtExceptionPreHandler(PluginManagerImpl.PluginExceptionHandler)    位置: 类 Thread

解决方案:

  1. 由于plugin插件,目前在手机侧和其他设备都没有引用,此处代码本身没有被用到,因此可以注释
  2. 如果不期望注释,可以引入对应的jar进来,该API位于
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java 代码被编译到 ojluni-phony 组件中 http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/Android.mk

源码过程记录

项目设置为私有,如果有需要请留言沟通。

https://gitee.com/big4panda/systemui

后续工作

  1. 减少jar的依赖,telephony-common 于framework.jar 存在重合,后续考虑将telephony-common用到的API合并到framework.jar中
  2. 由于作者从事的是车机上的systemUI,其功能相比手机简单,后续将用不到的功能和模块进行精简,做到各个子服务按需启停,达到APK瘦身高效的目的。

背景

当前作者的项目中Android APP只有SystemUI仍然需要跟随源码编译,主要是因为SystemUI没法单独在Android Studio编译通过,缺少的依赖太多了。所以只能通过编辑器写完代码后,合并到系统源码中单编出 APK 再验证,整个过程繁琐且耗时。如果有办法可以像开发普通 App 那样在 Android Studio 中开发系统 App,并且还能随时跟系统源码保持一致,相信能解决编译和调试难的问题。因此本文期望在本地Windows环境下对SystemUI进行二进制编译构建,同时代码开发和调试也在本地进行,更进一步,期望SystemUI工程不依赖Android版本,做到跨版本兼容。

目标

Android SystemUI 工程从源码构建方式切换到二进制独立构建编译,在Android 中修改代码编译验证后即可将代码push到服务器。

本文阅读对象

从事Android 项目开发,工程构建的研发工程师。本文虽然是基于Android9.0 源码进行说明,但其解耦隔离方案可以扩展到其他Android 工程(例如Settings)和其他Android版本(Android 10.0)。

前提条件

  1. Android 9.0 源码
  2. 完整编译过SystemUI工程
  3. 熟悉和了解Android gradle 编译APK的流程

创建Android Studio工程

创建一个新的工程

选择No Activity
包名:com.android.systemui

创建module

类型为Android Library,
需要创建四个module,为什么是这四个,可以看SystemUI的Android.mk和源码下的AndroidManifest.xml文件的个数,一个AndroidManifest.xml文件需要新建一个模块

模块 包名
keyguard com.android.keyguard
plugin com.android.systemui.plugins
shared com.android.systemui.shared
settingslib com.android.settingslib

删除不用的文件和资源

删除每个模块下的testandroidTest目录
删除gradle – dependencies下的所有内容

将每个模块的build.grale 中属性配置为当前API级别

compileSdkVersion 28 buildToolsVersion '28.0.3'

关闭AndroidX

android.useAndroidX=false android.enableJetifier=false

​ 至此,Android Studio工程结构如下

SystemUI 源码编译切换为 Android Studio 独立编译

小技巧:

由于过程较为复杂,建议将工程通过git进行管理,这样一旦有推进可以及时在本地提交,一旦有问题可以及时回退。

拷贝源码文件

此步骤需要根据SystemUI下的Android.mk文件进行详细分析,逐个分析上述中app工程和四个module的源码,和组件依赖。

SystemUI 源码编译切换为 Android Studio 独立编译

源文件 路径(相对于项目根目录) 说明
frameworks/base/packages/SystemUI SystemUI_SRC
frameworks/base/packages/SettingsLib SettingsLib_SRC F4
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar jar/framework.jar F3
out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/classes.jar jar/telephony-common.jar F2
out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar jar/core-libart.jar F4
out/target/common/obj/JAVA_LIBRARIES/core-oj_intermediates/classes.jar jar/core-oj.jar F4

下面这部分文件是aidl,proto,logtags文件生成的java文件,直接拷贝到新建的模块目录下。

源文件 路径(相对于项目根目录)
out/target/common/obj/JAVA_LIBRARIES/SystemUI-proto_intermediates/proto/src/* app/src/main/java
out/target/common/obj/JAVA_LIBRARIES/SystemUI-tags_intermediates/logtags/src/* app/src/main/java
out/target/common/obj/APPS/SystemUI_intermediates/aidl/src/* app/src/main/java
out/target/common/obj/JAVA_LIBRARIES/SystemUISharedLib_intermediates/aidl/src/* shared/src/main/java
***/corejavaandroidhardwarefingerprint app/src/main/java

由于SystemUI的手机和车机共用一个工程,只不过存在资源差异,这部分是通过Overlay机制来实现的。如果有同学需要基于车机项目编译,那么需要将overlay资源也配置。至于资源路径,我们可以从编译脚本分析得到

Android Car的编译过程分析,选择car的版本,我们跟踪执行的脚本如下

build/envsetup.sh devicegenericcarcommon\car.mk packages/services/Car/car_product/build/car.mk packages/services/Car/car_product/build/car_base.mk PRODUCT_PACKAGE_OVERLAYS += packages/services/Car/car_product/overlay

从这里我们可以看出,Overlay的资源在packages/services/Car/car_product/overlay 目录下,这里我们将SettingsLib 和SystemUI下面的资源拷贝到工程目录下

源文件 路径(相对于项目根目录)
packagesservicesCarcar_productoverlayframeworksbasepackagesSettingsLib overlay/SettingsLib
packagesservicesCarcar_productoverlayframeworksbasepackagesSystemUI overlay/SystemUI

至此项目目录结构如下

SystemUI 源码编译切换为 Android Studio 独立编译

配置项目

  1. 项目工程build.gradle 文件配置
buildscript {     repositories {         maven { url 'https://maven.aliyun.com/repository/public' } //加快下载         google()     }     dependencies {         classpath "com.android.tools.build:gradle:4.1.1"     }  }  allprojects {     repositories {         maven { url 'https://maven.aliyun.com/repository/public' } //加快下载         google()     }      gradle.projectsEvaluated {         tasks.withType(JavaCompile) {             // 如果有多个jar需要提升优先级,用分号隔开,jar/core-oj.jar             options.compilerArgs.add('-Xbootclasspath/p:jar/framework.jar;')         }     } }
  1. app模块的gradle build.gradle配置
plugins {     id 'com.android.application' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         applicationId "com.android.systemui"         minSdkVersion 28         targetSdkVersion 28         versionCode 2         versionName "2.0"     }      signingConfigs {          main {             storeFile file("../keystore/platform.jks") //签名文件路径             storePassword "XXX"             keyAlias "XXX"             keyPassword "XXX"         }     }      buildTypes {         release {             minifyEnabled false             signingConfig signingConfigs.main //添加这一行             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }          debug {             minifyEnabled false             signingConfig signingConfigs.main //添加这一行             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }      }      compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main {             res.srcDirs = [                     '../SystemUI_SRC/res',                     '../overlay/SystemUI/res',                     '../SystemUI_SRC/res-keyguard',                     '../overlay/SystemUI/res-keyguard',             ]              java.srcDirs = [                     'src/main/java',                     '../SystemUI_SRC/src',             ]              manifest.srcFile '../SystemUI_SRC/AndroidManifest.xml'         }     }      lintOptions {         abortOnError false         checkReleaseBuilds false     }      aaptOptions {             additionalParameters '--auto-add-overlay'     }  }  dependencies {      compileOnly files('../jar/framework.jar')     compileOnly files('../jar/core-oj.jar')     compileOnly files('../jar/telephony-common.jar')     compileOnly files('../jar/core-libart.jar')     compileOnly files('../jar/android.car.jar')     compileOnly files('../jar/bluetoothkit.jar')     //需要那些依赖,可以在Android.mk里看到     //noinspection GradleCompatible     implementation 'com.android.support:support-v4:28.0.0'     implementation 'com.android.support:car:28.0.0-alpha5'     implementation 'com.android.support:appcompat-v7:28.0.0'     implementation 'com.android.support:recyclerview-v7:28.0.0'     implementation 'com.android.support:preference-v7:28.0.0'     implementation 'com.android.support:mediarouter-v7:28.0.0'     implementation 'com.android.support:palette-v7:28.0.0'     implementation 'com.android.support:preference-v14:28.0.0'     implementation 'com.android.support:leanback-v17:28.0.0'     implementation 'com.android.support:slices-builders:28.0.0'     implementation 'com.android.support:slices-core:28.0.0'     implementation 'com.android.support:slices-view:28.0.0'     implementation 'com.google.protobuf:protobuf-java:3.12.2'     implementation group: 'com.google.protobuf.nano', name: 'protobuf-javanano', version: '3.1.0'     //把几个模块包含进来     implementation project(path: ':settingslib')     implementation project(path: ':plugin')     implementation project(path: ':shared')     implementation project(path: ':keyguard') }
  1. keyguard 的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false            //add  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }  }  dependencies {  }
  1. plugin的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false            // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             java.srcDirs = [                     '../SystemUI_SRC/plugin/src',             ]         }     }  }  dependencies {     compileOnly files('../jar/framework.jar') }
  1. settingslib的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             res.srcDirs = [                     '../SettingsLib_SRC/res',             ]              java.srcDirs = [                     '../SettingsLib_SRC/src',             ]         }     } }  dependencies {     compileOnly files('../jar/core-libart.jar')      //lifecyce-runtime 包含在appcompat中26.1中及以上     implementation 'com.android.support:appcompat-v7:28.0.0'     implementation 'com.android.support:recyclerview-v7:28.0.0'     implementation 'com.android.support:preference-v7:28.0.0'     implementation 'com.android.support:preference-v14:28.0.0'  }
  1. shared的build.gradle配置
plugins {     id 'com.android.library' }  android {     compileSdkVersion 28     buildToolsVersion '28.0.3'      defaultConfig {         minSdkVersion 28         targetSdkVersion 28         versionCode 1         versionName "1.0"          consumerProguardFiles "consumer-rules.pro"     }      buildTypes {         release {             minifyEnabled false             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'         }     }     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }      sourceSets {         main{             java.srcDirs=[                     'src/main/java',                     '../SystemUI_SRC/shared/src',             ]         }     } }  dependencies {      compileOnly files('../jar/framework.jar') }

至此项目框架已经搭建完成,但是里编译成功仍然有所差距。

解决编译问题

这里把作者编译过程的出现的问题记录如下

问题1:string重复资源,value/string.xml中相同name的字符串存在多份(default,tablet,device),由于Android Studio不支持这种方式。

解决方案:

  1. 由于涉及到多国语言,手动删除效率低下,可以通过脚本,将重复的字符串删除,只保留default类型的字符串

  2. 如果APK需要同时编译差异化的APK版本,可以将重复的字符串提取出来,通过设置不同product来编译出不同的APK

删除脚本比较简单

   echo delete $1    find ./ -name "strings.xml" |xargs sed -i "/$1/d"

问题2: Error:Found item Attr/intent more than once time

解决方案:如果只是在 App 内部或者 Lib 内部,是可以使用同名属性的,处理方法为,在 resources 顶部定义公用的属性,在 declare-styleable 内进行引用,如:

<?xml version="1.0" encoding="utf-8"?>  <resources>  <attr name="common_bar" format="integer"/>  <declare-styleable name="LocalAttr1">  <attr name="common_bar"/>  </declare-styleable>  <declare-styleable name="LocalAttr2">  <attr name="common_bar"/>  </declare-styleable>  </resources>

注意 LocalAttr1 和 LocalAttr2 的 common_bar 不能带 format,否则会变为属性声明,导致冲突。LocalAttr1 和 LocalAttr2 里面 common_bar 的 format 为外部声明的 integer,不能具有不同的类型。在外部声明 common_bar 时可以一次性声明多种format,如:

<attr name="common_bar" format="integer|string"/>

问题3:百分号的引用问题

Multiple annotations found at this line:  error: Multiple substitutions specified in non-positional format; did you mean to add  the formatted="false" attribute?  error: Unexpected end tag string

解决方案:出现这个错误的原因主要是因为strings字串中包含百分号(%),主要将字符串声明为无需格式化

<string 标签上增加属性:formatted="false">

问题4:orientaion 名称与系统名称重复

解决方案:本地自定义名称定义不和系统名称相同,例如

<attr name="orientation1">     <enum name="horizontal" value="0" />     <enum name="vertical" value="1" /> </attr>

问题5:无法引用系统资源,保持如下

AAPT: error: resource android: dimen/rounded_corner_radius not found.  AAPT: error: resource android: dimen/rounded_corner_radius_bottom not found.  AAPT: error: resource android: dimen/rounded_corner_radius_top not found

解决方案:找到资源的值,直接硬编码

<dimen name="rounded_corner_radius>0dp</dimen>

问题6:

SystemUI_SRCsrccomandroidsystemuipluginsPluginManagerImpl.java:97: 错误: 找不到符号   Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);    符号:   方法 setUncaughtExceptionPreHandler(PluginManagerImpl.PluginExceptionHandler)    位置: 类 Thread

解决方案:

  1. 由于plugin插件,目前在手机侧和其他设备都没有引用,此处代码本身没有被用到,因此可以注释
  2. 如果不期望注释,可以引入对应的jar进来,该API位于
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java 代码被编译到 ojluni-phony 组件中 http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/Android.mk

源码过程记录

项目设置为私有,如果有需要请留言沟通。

https://gitee.com/big4panda/systemui

后续工作

  1. 减少jar的依赖,telephony-common 于framework.jar 存在重合,后续考虑将telephony-common用到的API合并到framework.jar中
  2. 由于作者从事的是车机上的systemUI,其功能相比手机简单,后续将用不到的功能和模块进行精简,做到各个子服务按需启停,达到APK瘦身高效的目的。

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » SystemUI 源码编译切换为 Android Studio 独立编译求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们