废话不多说,直接进入正题
一、移植前的准备工作
1.一套mtk的系统源码(包含了Settings,settingslib,setupcompat,setupdesigin,wifitracklib等)
2.Android Studio环境
3.最好使用ubuntu系统,或者虚拟机装ubuntu系统
4.确保网络通畅,因为有很多依赖需要下载,最好能翻墙,否则有些下载失败
5.建议gradle版本不要用太高的,我用的8.0的,agp也用的8.0,kotlin版本用的1.8.10
二、正式开始
1.在AS里面新建一个Android项目,包名为com.android.settings,把源码Settings里面的src,res,res-export,res-product,res_ext,libs,还有protos以及配置文件导入工程,覆盖。
Settings源码目录
2.Settings依赖很多子模块,其中最大的一块SettingsLib,源码如下图
新建一个module,名字为settingslib,包名以源码目录中的AndroidManifest.xml里面的为准,com.android.settingslib
删除src下面的androidTest跟test,保留main目录,然后把SettingsLib目录下的src,res以及AndroidManifest的配置文件拷贝到该module,其中SettingsLib又依赖很多子模块,可以查看bp文件
把这些子模块依次以module的形式引入,有些子模块是直接在SettingsLib这个目录下,有些则不在,比如iconloader,还有setupcompat,setupdesign,wifitracklib,这些模块自己去源码里面找,然后一个个以module的形式引入,一共差不多有40来个。每次引入一个,单独调试编译确保最小的单元能编译通过。
3.举个具体的子模块,SettingsLib里面的spa模块,按照上面说的创建好module,这里命名为SpaLib,导入src以及res,配置文件,跟bp文件去配置gradle,该模块使用了compose,需要配置compose的运行环境,如果kotlin版本低于2.0,则采用以下配置
composeOptions {
kotlinCompilerExtensionVersion '1.4.2'
}
buildFeatures {
compose true
}
此处需要注意,kotlinCompilerExtensionVersion这个版本号要跟kotlin的版本对应,有个对应关系表:
我项目里kotlin使用的是1.8.10版本,所以这里compose编译版本用的是1.4.2,这里关系一定要对应,否则编译会失败,其他的一些依赖根据bp文件引入相应的库就行,大多为androidx库,最终的grade配置如下:
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
// alias(libs.plugins.compose.compiler)
}
android {
namespace 'com.android.settingslib.spa'
compileSdk 34
defaultConfig {
minSdk 29
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
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
}
kotlinOptions {
jvmTarget = "1.8"
}
composeOptions {
kotlinCompilerExtensionVersion '1.4.2'
}
buildFeatures {
compose true
}
}
dependencies {
implementation platform(libs.androidx.compose.bom)
implementation libs.appcompat
implementation libs.material
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
implementation libs.slice.builders
implementation libs.slice.core
implementation libs.slice.view
implementation libs.compose.animation
implementation libs.compose.material3
implementation libs.compose.material.icons.extended
implementation libs.compose.runtime
implementation libs.compose.runtime.livedate
implementation libs.compose.ui.ui.tooling.preview
implementation libs.lifecycle.livedata.ktx
implementation libs.lifecycle.runtime.compose
implementation libs.navigation.compose
implementation libs.lottie.compose
implementation libs.mpandroidchart
implementation libs.androidx.activity.compose
implementation libs.compose.ui
//支持koltin
implementation libs.androidx.core.ktx
// implementation libs.kotlin.stdlib.jdk8
}
这个模块使用了kotlin,所以要引入kotlin插件
4.还有一些模块调用了sdk里面隐藏的接口,或者sdk里面没有的接口,这些接口都在framework.jar里面,需要引入这个jar,比如SettingsLib模块下的子模块Utils,如果没有引入framework,会报如下异常:
PackageManager.MATCH_ANY_USER该属性是隐藏的,引入framework.jar后,还必须要提高其优先级,才能使用隐藏的属性或者方法,提高jar包优先级方法如下:
该配置可以方法项目级别下的build.gradle文件,也就是最外层的gradle,这样就不需要在每个子模块都配置。
5.还有一些模块使用了aidl,比如setupcompat模块,找到setupcompat模块,其目录结构如下:build.gradle需要增加aidl的配置:
apply plugin: 'com.android.library'
android {
// Not specifying compileSdkVersion here so clients can specify it; must be at least Q
namespace 'com.google.android.setupcompat'
compileSdk 34
defaultConfig {
minSdkVersion 29
targetSdkVersion 34
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.flags'
}
}
sourceSets.main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src/main/java','partnerconfig/java']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_9
targetCompatibility JavaVersion.VERSION_1_9
}
buildFeatures {
aidl = true
}
}
dependencies {
implementation libs.annotation
//在PartnerCustomizationLayout使用了,暂时有问题,注释掉了
implementation libs.errorprone
}
其他30多个模块都是大同小异,全部一一引入,调试编译通过。
所有子模块都引入并能单独编译通过后,开始调试settingslib模块,直接编译该模块,提示异常提示缺哪个类,在哪个模块下,就在gradle引入这个模块,最终的grade配置如下:
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
}
android {
namespace 'com.android.settingslib'
compileSdk 34
defaultConfig {
minSdk 29
targetSdk 34
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
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
}
kotlinOptions {
jvmTarget = "1.8"
}
lintOptions {
checkReleaseBuilds false
abortOnError false
}
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs.add('-Xbootclasspath/p:' + getRootDir().getPath() + '/libs' + '/framework.jar:'
+ getRootDir().getPath() + '/app/libs/framework-bluetooth.jar:'
+ getRootDir().getPath() + '/app/libs/framework-wifi.jar:'
+ getRootDir().getPath() + '/app/libs/framework-connectivity.jar:'
+ getRootDir().getPath() + '/app/libs/framework-connectivity2.jar:'
)
}
}
}
dependencies {
implementation libs.appcompat
implementation libs.material
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
//android x , add by huangxiaofeng
implementation libs.annotation
implementation libs.coordinatorlayout
implementation libs.core.core
implementation libs.fragment
implementation libs.lifecycle.runtime
implementation libs.loader
implementation libs.localbroadcastmanager
implementation libs.preference
implementation libs.recyclerview
implementation libs.zxing.core
implementation libs.room.runtime
implementation libs.androidx.core.ktx
implementation libs.window
// implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
// implementation libs.legacy.support.v4
// implementation libs.legacy.preference.v14
// implementation libs.mediarouter.nodeps
// implementation 'androidx.appcompat:appcompat-resources:1.6.1'
compileOnly files('../app/libs/iconloader.jar')
compileOnly files('../libs/framework.jar')
compileOnly files('../app/libs/core-libart.jar') //for libcore.io.Streams
compileOnly files('../app/libs/core-icu4j.jar')
compileOnly files('../app/libs/framework-bluetooth.jar')
compileOnly files('../app/libs/framework-connectivity.jar')
compileOnly files('../app/libs/framework-connectivity2.jar')
compileOnly files('../app/libs/framework-tethering.jar')
compileOnly files('../app/libs/framework-configinfrastructure.jar')
compileOnly files('../app/libs/framework-wifi.jar')
// compileOnly files('../app/libs/ext.jar')
// compileOnly files('../app/libs/framework-wifi-util-lib.jar')
//extern dependency
implementation project(':WifiTrackerLibRes')
implementation project(':setupdesign') //org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 -> 1.7.1 导致升级了版本,根本原因是该依赖implementation libs.window的版本为1.2.0导致,降低为1.0.0
implementation project(':setupcompat')
implementation project(':iconloader')
//inner module
implementation project(':SettingsLibHelpUtils')
implementation project(':SettingsLibRestrictedLockUtils')
implementation project(':SettingsLibActionBarShadow')
implementation project(':SettingsLibAppPreference')
implementation project(':SettingsLibSearchWidget')
implementation project(':SettingsLibSettingsSpinner')
implementation project(':SettingsLibIllustrationPreference')
implementation project(':SettingsLibLayoutPreference')
implementation project(':SettingsLibMainSwitchPreference')
implementation project(':SettingsLibActionButtonsPreference')
implementation project(':SettingsLibEntityHeaderWidgets')
implementation project(':SettingsLibBarChartPreference')
implementation project(':SettingsLibProgressBar')
implementation project(':SettingsLibAdaptiveIcon')
implementation project(':SettingsLibRadioButtonPreference')
implementation project(':SettingsLibSelectorWithWidgetPreference')
implementation project(':SettingsLibDisplayUtils')
implementation project(':SettingsLibUtils')
implementation project(':SettingsLibEmergencyNumber')
implementation project(':SettingsLibTopIntroPreference')
implementation project(':SettingsLibBannerMessagePreference')
implementation project(':SettingsLibFooterPreference')
implementation project(':SettingsLibUsageProgressBarPreference')
implementation project(':SettingsLibCollapsingToolbarBaseActivity')
implementation project(':SettingsLibTwoTargetPreference')
implementation project(':SettingsLibSettingsTransition')
implementation project(':SettingsLibButtonPreference')
implementation project(':SettingsLibDeviceStateRotationLock')
implementation project(':SettingsLibProfileSelector')
}
你可能需要编译若干次,每次都会报某某类找不到,先在当前项目全局查找,看是不是在已经引入的某个子模块下,如果找到,则引入相应的子模块即可,如果没有找到,可能在系统源码里面的某个jar里面,在整编完系统源码后,几乎所有的jar都会在如下目录
out/soong/.intermediates
out/target/common/obj/JAVA_LIBRARIES
比如import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE;
提示这个找不到,则可以到上述目录下,通过命令:grep android.uwb.UwbManager.class -rn .找到该类所在的jar包,结果如下:
这里我已经把jar包在系统的out目录下找到并放到了其他路径下,所以此处截图的结果并不是在上述提到的两个路径下。
总共大概有40个jar需要引入,settingslib能编译通过后,基本上就完成了一半的移植工作。
移植的过程中需要注意的点以及可能出现的问题:
1.jdk兼容性问题,我建议所有模块的gradle里面配置java的编译环境jdk为1.8,kotlin的也为1.8,
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
否则会出现一些莫名其妙的错误,比如framework里面的一些接口找不到,因为我的jar包是另外的同事提供的,他编系统源码的过程使用的是jdk17的环境,所以他提供的jar包都是jdk17的,其中有个jar,SettingsLibActivityEmbedding.jar,引用其中的内联函数,报了一个异常:
jdk不兼容导致,后来解决方案是不使用该jar,在如下依赖找到了这个接口
2.一些资源找不到,比如字符串,可以通过如下配置,可能解决:
在grade.properties中,增加一行配置
android.nonTransitiveRClass=false
3.有些子模块如:SettingsLibBannerMessagePreference,gradle配置没问题,但就是提示找不到settingslib_preferred_minimum_touch_target,可以尝试暂时把该module改为application,不再以library的形式引入,居然就编译通过了,我暂时没搞懂什么原因,在最后调试app的时候,再把对应模块改回以library的形式引入就行,参考如下大佬的文章:添加链接描述
4.Error while processing /home/ls/AndroidStudioProjects/Lens_Settings/setupdesign/main/res/drawable-anydpi-v21/sud_navbar_ic_more.xml : Width (0) and height (0) cannot be <= 0
定义的dimen明明不为0,却报这个异常,无奈硬编码设置宽高,暂时通过编译
5.找不到该资源android:color=“?androidprv:attr/colorSurfaceHighlight”
目前as好像不支持这种写法:?androidprv,我的处理方式是先注释,因为不多,先确保工程能编译通过.
6.implementation ‘com.github.PhilJay:MPAndroidChart:v3.1.0’ 依赖失败原因,要先配置maven,根据官网去配置
7.AS尚不能处理product属性,所以不能通过此属性来区分,出现的重复资源,需要去除,因为涉及很多字符串,需要使用正则表达式去删,不能手动去删,极容易误删,主要命令:
<string name="(.*?)" product="nosdcard" msgid="(.*?)">"(.*?)"</string>
<string name="(.*?)" product="nosdcard">(.*?)</string>
(.*?)<string name="(.*?)" product="tablet" msgid="(.*?)">(.*?)</string>(.*?)
(.*?)<string name="(.*?)" product="tablet">(.*?)</string>(.*?)
(.*?)<string name="(.*?)" product="tablet">\n(.*?)\n(.*?)</string>(.*?)
具体参考文章:添加链接描述,感谢大佬
8.org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering
该异常是compose的环境没有配对
9.异常:static import only from classes and interfaces
该异常都是jdk不兼容导致,明明提示的类都在framework.jar里面,但却报了该异常,build.gradle中的jdk都配置为1.8就行.
基本上就这些,最后附上自测能编译出apk的完整版项目结构:
用到的jar包
app module下:
项目根目录下:
--------------------------------------------记录运行时异常-----------------------------------------------------------------------
2024.06.19
异常一:系统资源找不到
报错堆栈:
2024-06-19 10:27:33.588 27985-27985 AndroidRuntime com.android.settings E FATAL EXCEPTION: main
Process: com.android.settings, PID: 27985
android.content.res.Resources$NotFoundException: Drawable com.android.settings:drawable/abc_dialog_material_background with resource ID #0x7f080072
Caused by: java.lang.UnsupportedOperationException: Can't convert value at index 0 to dimension: type=0x4, theme={InheritanceMap=[id=0x7f1403accom.android.settings:style/Theme.AlertDialog, id=0x7f1403adcom.android.settings:style/Theme.AlertDialog.Base, id=0x7f1403b7com.android.settings:style/Theme.AppCompat.DayNight.Dialog.Alert, id=0x7f1403c3com.android.settings:style/Theme.AppCompat.Light.Dialog.Alert, id=0x7f14007bcom.android.settings:style/Base.Theme.AppCompat.Light.Dialog.Alert, id=0x7f14007acom.android.settings:style/Base.Theme.AppCompat.Light.Dialog, id=0x7f1400cacom.android.settings:style/Base.V21.Theme.AppCompat.Light.Dialog, id=0x7f1400e3com.android.settings:style/Base.V7.Theme.AppCompat.Light.Dialog, id=0x7f140078com.android.settings:style/Base.Theme.AppCompat.Light, id=0x7f1400dfcom.android.settings:style/Base.V28.Theme.AppCompat.Light, id=0x7f1400dccom.android.settings:style/Base.V26.Theme.AppCompat.Light, id=0x7f1400d6com.android.settings:style/Base.V23.Theme.AppCompat.Light, id=0x7f1400d4com.android.settings:style/Base.V22.Theme.AppCompat.Light, id=0x7f1400c9com.android.settings:style/Base.V21.Theme.AppCompat.Light, id=0x7f1400e2com.android.settings:style/Base.V7.Theme.AppCompat.Light, id=0x7f1401aecom.android.settings:style/Platform.AppCompat.Light, id=0x7f1401b9com.android.settings:style/Platform.V25.AppCompat.Light, id=0x1030241android:style/Theme.Material.Light.NoActionBar, id=0x1030237android:style/Theme.Material.Light, id=0x103000candroid:style/Theme.Light, id=0x1030005android:style/Theme], Themes=[com.android.settings:style/Theme.AlertDialog, forced, com.android.settings:style/SettingsPreferenceTheme, not forced, com.android.settings:style/Theme.SubSettings, forced, com.android.settings:style/SetupWizardPartnerResource, forced, android:style/Theme.DeviceDefault.Light.DarkActionBar, forced]}
at android.content.res.TypedArray.getDimensionPixelSize(TypedArray.java:795)
at android.graphics.drawable.GradientDrawable.updateDrawableCorners(GradientDrawable.java:1663)
at android.graphics.drawable.GradientDrawable.applyThemeChildElements(GradientDrawable.java:1571)
at android.graphics.drawable.GradientDrawable.applyTheme(GradientDrawable.java:1467)
at android.graphics.drawable.DrawableWrapper.applyTheme(DrawableWrapper.java:165)
at android.graphics.drawable.InsetDrawable.applyTheme(InsetDrawable.java:150)
at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:696)
at android.content.res.Resources.loadDrawable(Resources.java:1004)
at android.content.res.Resources.getDrawableForDensity(Resources.java:994)
at android.content.res.Resources.getDrawable(Resources.java:933)
at android.content.Context.getDrawable(Context.java:948)
at androidx.core.content.ContextCompat$Api21Impl.getDrawable(ContextCompat.java:1012)
at androidx.core.content.ContextCompat.getDrawable(ContextCompat.java:522)
at androidx.appcompat.widget.ResourceManagerInternal.getDrawable(ResourceManagerInternal.java:149)
at androidx.appcompat.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:480)
at androidx.appcompat.widget.TintTypedArray.getDrawableIfKnown(TintTypedArray.java:94)
at androidx.appcompat.app.AppCompatDelegateImpl.attachToWindow(AppCompatDelegateImpl.java:872)
at androidx.appcompat.app.AppCompatDelegateImpl.<init>(AppCompatDelegateImpl.java:342)
at androidx.appcompat.app.AppCompatDelegateImpl.<init>(AppCompatDelegateImpl.java:303)
at androidx.appcompat.app.AppCompatDelegate.create(AppCompatDelegate.java:267)
at androidx.appcompat.app.AppCompatDialog.getDelegate(AppCompatDialog.java:178)
at androidx.appcompat.app.AppCompatDialog.<init>(AppCompatDialog.java:57)
at androidx.appcompat.app.AlertDialog.<init>(AlertDialog.java:98)
2024-06-19 10:27:33.588 27985-27985 AndroidRuntime com.android.settings E at androidx.appcompat.app.AlertDialog$Builder.create(AlertDialog.java:983)
at androidx.preference.PreferenceDialogFragmentCompat.onCreateDialog(PreferenceDialogFragmentCompat.java:161)
at androidx.fragment.app.DialogFragment.prepareDialog(DialogFragment.java:925)
at androidx.fragment.app.DialogFragment.onGetLayoutInflater(DialogFragment.java:839)
at androidx.fragment.app.Fragment.performGetLayoutInflater(Fragment.java:1756)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:539)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2103)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2004)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1941)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:661)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8194)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
分析:根据日志,是在使用AlertDialog时,找不到对应的资源,在配置文件manifest的application节点
使用了主题Theme.Settings,里面配置了alertDialogTheme
原因在这里,系统属性config_dialogCornerRadius找不到,我这里暂时使用硬编码写死28dp,暂时解决这个问题,其本质原因,我猜测是因为AS中编译使用的是原生的SDK,而系统可能有新增的资源,比如如果你们是基于mtk平台的话,就会新增很多系统资源,这就导致,原生sdk中的资源id跟系统生成的对应不上,所以找不到,可以使用系统编译出来的android.jar,替换sdk中的android.jar,去验证这个问题是否解决。我目前没有这个环境,暂未验证(2024.07.09通过另一个string资源不对,已验证)。
----------------------------------update 2024.07.09 begin-------------------------------------------------------
引用系统资源id不对,@*android:string/data_saver_description 该字符串取值不对,原因“异常一”已说明,这里通过手动编译sdk,替换原生sdk中的android.jar,完美解决
编译sdk的方法:
1.需要整编一遍系统的源码
2.在源码根目录执行以下命令
lunch sdk-eng
make sdk
编译成功如下,注意输出的路径Package SDK: out/host/linux-x86/sdk/sdk/android-sdk_eng.ls_linux-x86.zip:
解压该zip,进入platforms-android-34,复制android.jar,替换android studio里面使用的原生sdk中对应platforms-android-34里面的android.jar,重新编译apk,解决
----------------------------------update 2027.07.09 end-------------------------------------------------------
异常二:JAR依赖冲突
本地JAR:guava.jar
远程依赖:com.google.guava:listenablefuture:1.0远程依赖可在grade通过如下配置去除:
configurations {
all*.exclude group: 'com.google.guava', module: 'listenablefuture'
}
异常三:使用implement依赖的JAR,编译过程中报如下异常:
com.android.tools.r8.internal.jb: Absent Code attribute in method that is not native or abstract
一般这种是JAR有问题,随便打开JAR包里面的某个类,查看具体方法,是个空实现,替换一个有具体实现逻辑的JAR包,重新编译即可,这种空实现的JAR如果采用compileOnly依赖编译过程中是不会报错的,但运行过程中,会提示相关的方法找不到,报类似异常:NoSuchMethodError