编译调试Android系统原生App - 以Settings为例

0、前言:

Android原生系统带有许多原生的App,比如浏览器、录音机、计算器、设置等,有些时候,我们需要用到一些系统的功能,或者是对已有的功能做二次开发,比如我上学时给一个公司做过一个Launcher和Wizard,就需要用到系统设置中的某些功能,比如Wifi、声音、显示等功能,于是就需要从Settings源码中提取出需要的功能。

特别是公司自己定制Android系统,需要在上面做一些系统级的App的时候,原生App已有的功能就可以通过编译其源码的方式直接拿过来改改就能用,而且可用度很高。

以下内容就以Settings为例,主要介绍如何编译和运行调试Settings源码。

1、源码:

这里有两种情况,分为原生的和公司定制的系统。无论是原生的还是定制的,类似于Settings这样需要使用到系统级或隐藏API的App,都需要系统签名文件和编译系统源码后得到相应的jar包才可以在IDE中编译,因为标准SDK根本没有那些API可供调用。

举个栗子:

  • 闹钟、计算器这类的不需要额外的Jar包,得到源码后导入IDE就可以运行的。由于包名冲突,想调试要么删掉系统App,要么修改包名。
  • Settings、Launcher之类的就需要系统级的Jar包,SDK没有提供相应API。这类的想调试就必须删掉同包名的系统App了,修改包名会有意想不到的乱七八糟的问题出现。

(1)原生App:

网址:https://android.googlesource.com/
位置:platform/packages/apps/,可Ctrl+F搜索一下。点击想要编译的App,比如Settings。
使用Git克隆下来:比如 git clone https://android.googlesource.com/platform/packages/apps/Settings

需要额外的Jar就需要自己编译系统源码啦,这个是比较麻烦的,有兴趣可以试试自己编译定制自己的Android系统。

(2)定制:

  跟公司的底层工程师要代码。
  首先是“out/target/common/obj/JAVA_LIBRARIES/”文件夹,让底层人员整个文件夹压缩了给到你,虽然比较大(大概1G),但这是必须的,底层人员可能会说:你要什么再跟我说,扯淡,不要听他的,你就要全部。
  再来是要Settings的源码。

** 注意,既然是定制的,源码、jar、签名文件,还有系统都是一一对应的,你不能拿其他公司的系统签名来给你公司的系统app签名,这样无法运行的。 **

2、编译:

有了源码,下一步当然是要跑起来啦。

建议都使用Eclipse来编译,不要使用AS,因为AS编译大型的原生App能卡到你吐血,而且出错提示也不友好。但是用过AS的人都不想再碰Eclipse了有没有??别急,可以先用Eclipse编译过了,再贴到AS中,这样好很多,也很节省时间。

步骤:

初始化:

  • 新建工程,Compile With 选最大,Target和Min都选和你使用的系统一致,包名和原生app的包名一致,比如Settings的是com.android.settings。
  • 新建成功后,添加libs文件夹,删掉AndroidManifest.xml,清空res。使用Git初始化项目,添加ignore文件,提交init,方便出错时回退。

放入源码:

  • 拷贝Settings源码的AndroidManifest.xml、src、res到工程,选择Project -> Clean清理一下,在Console会得到很多错误。
  • git commit 提交一下,还是那句话,出错方便回退。

修正res错误:

  • 首先是res下的错误,可能每个语言的strings.xml文件都会报错,建议删掉其他不需要的语言,可以根据Console的报错信息,写个小程序批量删除,留下需要的,并修正错误即可。比如原生的代码可能会有string重复,去掉即可。我只留下了value和value-zh-rCN的strings.xml,因为其他语言的用不到。
  • 若是缺少Drawable文件,比如图片,有以下方式可以找回来:
    * Google搜索文件名,不要使用百度,百度搜不出来。
    * 去SDK文件夹下全局搜索。
  • 不要怕修改,以解决问题为优先。比如我编译后发现缺少了@*android:drawable/XXX文件,在SDK文件夹搜索后找到该文件,放到了res/drawable文件夹后,将其引用修改为@drawable/XXX即可。毕竟只是个资源文件而已。

  • 如果是缺少style,可以把style的名称贴到Google中,一般都能得到正确的结果。

  • 总之就是根据提示一直解决到res没有提示错误为止。解决完毕,git commit。

修正src错误:

  • 再来是src的错误,首先是必须导入的jar包,解压底层工程师给你的JAVA_LIBRARIES文件夹,里面有很多的子文件夹。
    第一个包:
    文件夹:framework_intermediates
    第二个包:
    文件夹:core_intermediates/core-libart_intermediates(也许是其他的名字)
    第三个包:
    文件夹:android-common_intermediates

          将以上文件夹中的classes.jar,为了好区分重命名为framework-classes.jar、core-classes.jar、common-classes.jar。放到一个方便查找的文件夹中,这三个jar包将作为User Library引用。不能作为常规的jar使用。如何引用请Google。
    
  • 需要注意的是:
    引用后,在Java Build Path/Order and Export中,framework需排在第一、core第二、common第三。

  • 然后再Clean一下,看看src还有什么错误,比方说com.android.setupwizard.Test类没有找到,就去JAVA_LIBRARIES文件夹搜索Test,再定位到其目录下,找到其classes.jar文件,重命名后放入libs中即可。其他的错误也是如此。

  • 有个比较特殊的类EventLogTags,在文件夹中根本没有找到想要的,其使用的都是一些静态变量。于是就直接在网上搜索静态变量的名称,比如EventLogTags.LOCK_SCREEN_TYPE,直接使用其值,虽然对不对,但是没有办法也只能先这样了。

  • 同理,也是一直解决到Clean后src没有错误为止,再git commit。

      建议不勾选:
          Window -> Preference -> Android -> Lint error Checking 的Run full error check ....
      这样有些warning和不影响运行的Lint error就不会提示,反正没事,眼不见心不烦。
    

3、尝试运行:

编译通过了,试着直接debug跑一下,在安装进设备的时候,就会有以下错误:

报错:

  • Unable to execute dex: Multiple dex files define Landroid/support/annotation/AnimRes;
    Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define Landroid/support/annotation/AnimRes;
    这是因为Annotation曾经是作为一个单独的库存在的,但是由于某些原因,在最新的v4中,已经包含了Annotation了,所以,就不需要Annotation的包了,如果你没有导入Annotation的包,就说明你导入了SDK,通过Java Build Path/Libraries,Remove掉就可以了。比如我使用的是Android N,就Remove掉AndroidN。
    如果发现还有这个问题,那就是v4和v13的包冲突了,v13的包是包含v4的,把v4的包去掉就可以了。

  • Installation error: INSTALL_FAILED_VERSION_DOWNGRADE:
    因为系统中已经存在了Settings了,所以无法安装。可以取得Root权限,删掉/system/app/Settings/Settings.apk文件,删除前记得保存一下,万一出错可以恢复。

    或者,通过右键选择 Android Tools -> Rename Application Package,重命名Package,如果你的项目是GBK编码的,重命名可能会失败,可以通过右键选择Properties -> Resource -> 选择UTF-8即可重命名成功。此方法需慎重使用,不一定全部app都适用,如果没有用到特殊的API就完全没问题。因为像Settings这样的重命名后会有很多奇奇怪怪的问题。
    
    如果要删除Settings.apk,提示:override rw-r--r-- root:root for 'Settings.apk'?,可以使用-rf:
      rm -rf Settings.apk
    
    提示:rm: Settings.apk: Read-only file system的话,可以:
      mount -o rw,remount /system
    
    重新挂载/system目录,然后就可以删除了,这里涉及到的跟Linux的文件权限系统有关,这里不再扩展。
    
    其实有个软件可以操作,如果你的设备已经Root,那么可以下载X-plore这个app,打开设置,选择Root权限访问,选择超级用户+挂载可写,即可轻松删除系统app,不过在删除之前,建议备份。
    
  • Installation error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED:
    AndroidMenifest.xml错误,据说可能是包名有大写字母,反正我没有。在网上查了一番,有人说是,把Activity之类的注册写上全名即可。但是我也没有这样的情况,后来找到有人说android:taskAffinity的问题,这个是啥自己搜索,我发现有些是"",有些是com.android.settings,我全更改了,可还是有这个错误。搞来搞去,原来是我重命名包名了,才会有这些问题,就把包名改回去了,删掉相应系统App后,这个问题就解决了。

  • 弹出Toast有问题,我在SettingsActivity中使用Toast是有问题的,报xml解析错误。也不知道咋回事,没有深入探索。

  • Installation error: INSTALL_FAILED_SHARED_USER_INCOMPATIBLE:
    原因:apk的AndroidManifest.xml中声明了android:sharedUserId="android.uid.system",但没有相应的系统签名。别急,接下来就说明如何签名的问题。

4、调试

使用到系统级API的,或者AndroidManifest.xml文件中声明了

android:sharedUserId="android.uid.system"

那么没有系统签名,直接debug签名运行是不行的,需要向底层工程师要系统的签名文件,在源码目录
build\target\product\security
下的platform.pk8platform.x509.pem,如果你想看此次编译Settings是否已成功了,可以适当的在入口加一下Log,然后导出未签名的apk,使用系统签名进行签名后,放到/system/app/下替换掉Settings.apk,然后重启系统,打开设置,看看Logcat是否输出里加入的Log。

系统签名转换成debug签名进行调试:

在不知道系统签名可以转换成debug签名前,老实说我一直都是用Log的方式调试,太特么痛苦了。现在知道后整个人都懵逼了。

我们都希望可以像调试普通app那样调试系统app,以下是如何通过opensslplatform.pk8platform.x509.pem转换成debug.keystore文件:

三个命令

  • 得到platform.priv.pem
    openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out platform.priv.pem -nocrypt

  • 得到platform.pk12
    openssl pkcs12 -export -in platform.x509.pem -inkey platform.priv.pem -out platform.pk12 -name androiddebugkey

    此过程提示输入密码,输入android
    
  • 生成debug.keystore
    keytool -importkeystore -deststorepass android -destkeypass android -destkeystore debug.keystore -srckeystore platform.pk12 -srcstoretype PKCS12 -srcstorepass android -alias androiddebugkey

此方法来自:http://curlog.com/2016/08/30/android-pk2debug-keystore/

Mac自带openssl,Linux和Win需要安装。

然后就可以使用得到的debug签名配置到eclipse后愉快的调试啦,当然,得先把系统中已经存在的app先删除掉。然后重启系统,至于如何配置eclipse的debug签名,请Google。

5、结语:

使用过AS后,当然希望在AS中也可以调试系统App,抽空再写篇相关编译和调试的文章。如果这篇文章帮到你了,给个赞呗。

你可能感兴趣的:(编译调试Android系统原生App - 以Settings为例)