ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Android 5.1 AOSP源码深度指南:从环境搭建到系统定制实战

Android 5.1 AOSP源码深度指南:从环境搭建到系统定制实战 简介Android 5.1Lollipop源码包面向Android系统开发者、应用框架研究者以及自定义ROM定制人群用于深入理解系统启动流程、内核调度、组件通信机制和多窗口特性。资源以zip压缩包形式提供大小约27.07MB当前标注文件总数为0未给出逐文件明细目录结构按内核kernel、安卓框架android、支持库androidx、Java标准库javax、第三方服务com、org、性能基准benchmarks、模拟环境mock_android、Java核心库及测试并发库junit、jsr166等模块划分便于分层查阅适合作为系统源码学习的基础素材。已有661人次浏览学习。借助该源码可厘清Linux内核与硬件交互、Activity Manager等核心服务实现、Java并发与单元测试机制同时可结合内存管理、电源效率和多窗口支持等改动为后续应用性能优化、系统级调优或定制ROM开发提供一手参考尤其适合有Android开发基础、想向系统层进阶的读者。 Android 5.1源码Lollipop这套东西我在好几个项目里翻来覆去折腾过今天把值得说的部分整理出来从怎么拉源码、目录要看哪里、编译怎么过到lollipop这代特有的坑一次讲清楚。不管你是要做系统定制、驱动移植还是单纯想搞懂Android框架到底怎么跑起来这篇文章都能给你一个可以照着走的路线。1. Android 5.1源码的框架价值与核心设计思路很多人拿到AOSP源码第一反应是直接找app层代码其实方向就偏了。Android 5.1作为Lollipop的维护版本在整个Android演进史里属于承上启下的位置上面延续了Material Design的整套视觉体系下面的大规模改动则是ART运行时全面替换Dalvik以及默认64位内核支持铺开。从源码角度讲这一版比4.4更适合用来做深入阅读因为关键的架构模块已经稳定但又比6.0以后保留了更多“老派”的简单感——没有那么多Jeankins、Soong等构建系统新品种的干扰。1.1 从系统启动角度看代码框架如果你把Android 5.1源码当作一本书最好的读法不是按字母序读而是跟一次开机流程走。按下电源键后Boot ROM加载Bootloader然后是Linux内核启动接着进入init进程解析init.rc拉起zygote进程。zygote之后就是Android世界的起点SystemServer进程里面装着一大堆系统服务ActivityManagerService、PackageManagerService、WindowManagerService等等。看framework/base/services/java/com/android/server/下面那几百个文件你会理解什么叫“一切皆服务”。值得多说一句的是Lollipop在启动流程上的一个变化zygote的启动从原来单进程拆成了zygote和zygote64两个专门处理32位和64位应用的兼容问题。这个设计在5.0引入5.1里做了大量修正如果你在代码里看到process/zygote相关的两个目录不要觉得是冗余那是为了兼容不同instruction set的App进程所必须的。1.2 Lollipop源码目录快速导航AOSP根目录看我建议按四个层次掌握基础架构层在frameworks/和system/目录。前者是核心框架包括Java层的framework-res、服务、内容提供器后者包含init、vold、adb、core等基础C模块。硬件抽象层在hardware/目录下libhardware、libhardware_legacy还有各家芯片厂商的HAL实现都在这里驱动要跟系统对接主要看这一层。中间件和运行库看external/和art/前者内置大量开源库后者就是这一代的主角ART运行时。最底层是在kernel/不过AOSP默认不带内核源码需要单独从对应芯片商或kernel.org同步。如果你关注界面和View重点看frameworks/base/core/java/android/view/、frameworks/base/packages/SystemUI/Material Design的视觉细节全在这些渲染逻辑里。2. 源码获取、分支选择与编译环境准备我在实际项目中见过太多人卡在第一步要么是源码下载到一半失败要么是下完源码编译一个多小时直接内存不足崩掉。准备阶段把这些硬性条件摆平后面会省心很多。2.1 分支选择与同步要点Android 5.1对应的AOSP分支最常用的是android-5.1.1_r38这是5.1.x系列的最后一个较大维护版本修复了大量安全补丁而且兼容市面上绝大多数基于5.1的机型适配。如果你的目标是研究原生机型或者做CTS/GTS认证优先选择这个tag如果只做简单的framework定制或个人学习任何5.1.x r几都行差异在安全补丁里不在架构上。同步源码时有个很实际的建议不要直接网盘下载别人打包的“完整源码包”。因为那种包很难验证完整性而且往往不包含repo清单对应的确切git历史后续你想切分支或者做git blame根本没法用。推荐的做法是用repo工具初始化并同步mkdir android-5.1.1 cd android-5.1.1 repo init -u https://android.googlesource.com/platform/manifest -b android-5.1.1_r38 repo sync -j8 -c这里-c参数表示只同步当前分支不带历史能省不少流量和时间。同步过程中的网络问题属于常见情况中断后重新执行repo sync即可它会断点续传。源码全部下完大概在30GB左右如果只看不编译可以按需精简目录但编译的话建议完整同步。2.2 硬盘、内存与操作系统要求编译Android 5.1对机器的要求我实测下来是这样磁盘空间AOSP源码加out目录建议预留80~100GB。源码30GBout目录30GBccache缓存另占20GB左右。内存16GB起步32GB才舒服。低于12GB编译到一半out of memory的概率非常高这时就得靠swap硬撑。操作系统官方支持Ubuntu 14.04和16.04但实测Ubuntu 20.04也能跑通前提是装好兼容库。核心依赖包的安装Ubuntu 16.04/18.04下大概是这么一串命令sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip要注意的是在Ubuntu 20.04及更高版本上lib32ncurses5-dev这个包已经找不到了需要用libncurses5-dev libncursesw5-dev替代。还有openjdk版本Android 5.1要求OpenJDK 7。如果系统默认是OpenJDK 8或11编译会报错可以用sudo apt-get install openjdk-7-jdk或者找一个JDK7的tar包拷进去再配置JAVA_HOME。这个坑反复出现一定要先确认java -version输出版本是1.7.x。3. 编译Android 5.1源码的完整流程与参数解析源码同步完依赖装好接下来就是围观的编译环节。Android 5.1这个时代还是Makefile体系的黄金时代没有后来那么多构建系统变形编译过程相对可预期。3.1 配置编译环境初始化编译环境这一步很多人忽略了这步没做后面lunch直接命令找不到source build/envsetup.sh执行完后lunch等命令才能用。然后选择编译目标lunch会弹出一个菜单让你选设备型号。如果想直接指定可以写成lunch aosp_arm64-eng这里aosp_arm64-eng中的aosp_是product名arm64是架构eng是构建类型。构建类型有三种eng表示工程版带root调试权限适合开发user则是正式用户版不带额外调试工具。个人测试和二次开发选eng最方便。3.2 开启ccache加速与系统参数调整编译一次Android全量工程通常在1~2小时视机器而异第二次能不能压缩到半小时关键在ccache。export USE_CCACHE1 export CCACHE_DIR/home/user/android-5.1/.ccache prebuilts/misc/linux-x86/ccache/ccache -M 50G然后把后面两行export写到~/.bashrc里下次开终端自动生效。ccache缓存的是C/C预处理后的编译结果框架层Java改动频繁但C层原生库编译结果可以复用实测第二次编译能节省50%~70%时间。如果机器内存紧张建议在编译前关闭图形桌面或者加一个8G的swap文件否则整机编译时内存很容易被打满。3.3 make指令与错误日志定位一切就绪后执行make -j8-j参数建议设置为CPU核心数×2例如4核8线程的CPU可以-j8。如果编译中途报错系统会打印错误文件路径但有时候日志刷屏太快看不清楚最好先指定输出日志再从头查看make -j8 21 | tee build.log关键的几个错误特征先提前说下Out of memory加swap或减少-j参数。No rule to make target同步不完整补repo sync。Jack server类报错这是5.1引入的Java编译服务器问题通常和JDK版本不匹配有关确认你用的OpenJDK 7。libncurses.so.5 cannot open系统缺32位兼容库安装前面列的依赖。整个编译产物在out/target/product/目录下生成的镜像文件包括boot.img、system.img、userdata.img和recovery.img。用模拟器或刷机工具验证镜像时记得确认编译架构和你要运行的设备架构匹配arm64的镜像不能直接跑在32位模拟器上。4. 核心子系统源码剖析你该重点读哪些模块源码能编译通过只是开始真正有含金量的是读懂那些决定系统行为的关键模块。在Android 5.1中我比较推荐花时间深读这四块。4.1 ART运行时与垃圾回收Android 5.1是ART全面替代Dalvik正式落地的版本所以art/目录是整个系统最能体现性能设计思路的地方。ART把应用在安装时或首次运行时编译成native机器码而不是像Dalvik那样每次运行时解释执行或JIT编译所以App的启动速度和执行效率都有明显提升。读ART建议从runtime/gc/collector/看起里面包含了各种垃圾回收策略的实现。如果你关心的是“为什么老手机上5.1比4.4更流畅”答案就在这些代码里。5.1里的GC相比5.0做了大量调优比如更好的并发标记、更短的应用停顿时间。代码里那些复杂的并发条件和状态机是硬骨头但啃下来对理解Java虚拟机内存管理帮助极大。4.2 Binder IPC通信机制Android系统进程间通信全靠Binder这也是Android相对传统Linux IPC最具特色的地方。源码重点看frameworks/native/libs/binder/和kernel/drivers/android/binder.c。框架层的Binder调用最终要通过/dev/binder设备节点进入内核内核驱动程序负责实际的数据拷贝与线程管理。很多人看完这块代码才明白为什么Android的跨进程调用这么快Binder只拷贝一次数据而传统管道和System V消息队列要拷贝两次。Framework层里ServiceManager也是通过Binder管理的整个服务的注册、查询、调用都得跟它打交道。4.3 ActivityManagerService与四大组件调度AMS是整个framework里最庞杂的服务之一新版本里大几千行的类一上来就通读容易劝退。我的建议是先看关键方法的调用链比如startActivity的整个流程App进程通过Binder调用AMS.startActivityAMS通过ActivityStackSupervisor完成任务栈管理再通过ProcessRecord和ApplicationThread回调到App进程执行生命周期。阅读时可以用“应用冷启动”为线索从Launcher点击图标开始一直追到Activity.onCreate执行完这条链走通后你对Android应用进程的完整生命周期和Linux进程管理之间的关系都会有很具体的理解。4.4 PowerManagerService与省电机制Lollipop这一代对功耗管理格外重视PowerManagerService里实现了wakeup、wakelock、Doze等逻辑。5.1里部分Doze机制还比较初版但它设定的框架——根据设备静止状态和屏幕关闭时间降低后台活动频率——直接影响到后来所有Android版本的省电策略设计。如果你有做系统级省电优化的需求这块值得逐行读看懂suspend、early_suspend和wakeup source的管理比在App层想办法省电要有效得多。5. 框架层二次开发常见改动场景实战源码下来了编译通了读也有方向了很多人的实际目的是做系统定制。这里我整理几个典型的二次开发场景都是基于5.1的真实可落地改动。5.1 默认语言、时区、开机动画修改最基础的三件套默认语言、默认时区、开机动画。默认语言和时区在build/target/product/full_base.mk里定义PRODUCT_PROPERTY_OVERRIDES \ persist.sys.languagezh \ persist.sys.countryCN \ persist.sys.timezoneAsia/Shanghai也可以直接在定制产品的mk文件里加这些属性。开机动画是bootanimation.zip放在device/厂商/产品/目录编译时会被打包进/system/media/。它是图片帧加desc.txt的zip包可以用ps等工具将视频拆帧然后按desc.txt定义的分辨率和帧率打包。5.2 系统应用预置与去除预置应用分两种情况预置可卸载和预置不可卸载。可卸载的预置应用只要把APK放到vendor/厂商/产品/packages/下并在产品的mk文件里加上PRODUCT_PACKAGES引用即可。不可卸载的要放到device/厂商/产品/app/目录下编译系统会把它们编进system分区用户无法通过普通方式卸载。去除系统应用时注意别只删APK了事。很多系统应用同时注册了service、contentprovider和权限如果只删了manifest文件系统启动时ServiceManager找不到对应服务会导致部分功能异常。稳妥的做法是在源码build配置里去掉对应PRODUCT_PACKAGES条目或注释掉AndroidManifest中的service注册而不是直接删文件。5.3 开机自启动系统服务流程修改做定制系统时经常会遇到“开机后自动启动一个后台服务”的需求。在5.1里可以像这样实现在frameworks/base/services/core/java/com/android/server/SystemServer.java的startOtherServices()方法里加入你自己的服务启动代码类似try { Slog.i(TAG, My Custom Service); ServiceManager.addService(my_custom_service, new MyCustomService(context)); } catch (Throwable e) { Slog.e(TAG, Failure starting My Custom Service, e); }然后新建自己的服务类放在frameworks/base/services/core/java/com/android/server/下。这种方式的服务属于系统服务不会被用户杀掉也可以通过ServiceManager让普通App调用。不过要注意系统服务写得不好会拖慢整个系统的启动速度尽量在服务里做异步操作不要让主线程卡在复杂初始化上。6. 内核源码与驱动适配协同Android整个系统最底层的部分就是内核5.1源码树里不含内核代码但设备要跑起来内核和HAL层必须跟AOSP版本匹配这是一项容易踩坑但被很多人忽略的工作。6.1 内核版本选择与打补丁Android 5.1通常搭载Linux 3.10或3.14内核高通的msm平台用的往往是3.10或3.18。如果你有一个旧设备想升级到5.1最稳妥的方式不是从零移植而是在同芯片平台的、已经适配过5.1的kernel基础上来。比如手头是msm8916平台就去找该平台官方release过5.1的kernel tag然后在上面做板级适配。刷内核补丁时可能涉及kernel specific的改动主要是arch/arm64/configs/下的defconfig文件里配置项的变化以及drivers/staging/android/里ion、ashmen等驱动版本对齐。ION内存在5.1之后是主流旧内核里可能有PMEM遗留必须在defconfig里关掉否则图形缓冲分配会出现诡异花屏或黑屏问题。6.2 HAL层与内核驱动的配合HAL接口定义在hardware/libhardware/include/hardware/下每个硬件模组通常有对应的.h头文件定义统一接口。以Camera为例5.1的Camera HAL必须实现camera_module_t和camera_device_t接口这些接口内部对应底层V4L2或更底层的厂商私有驱动接口。在调试摄像头相关问题时我会先用一个简单的V4L2测试程序直接打开/dev/video节点确认硬件驱动工作正常再去查HAL层的问题。把问题分层能节省大量时间不要一上来就在framework里打log。7. 常见问题与调试心得最后这部分我把这几年和Android 5.1打交道经常出现的问题以及对应的排查方式一次性列出来你可以直接照着对照。问题现象可能原因解决/排查方式编译爆内存Out of memory-j开太高物理内存不足改小-j数加swap别让桌面环境占内存Jack server编译失败JDK版本不匹配默认JDK8安装OpenJDK 7重新source清理out目录libncurses.so.5报错缺少32位兼容库安装libncurses5-dev或做软链接开机卡在第二屏framework服务崩溃或system_server起不来抓logcat查system_serverFATAL EXCEPTION相机黑屏/花屏ION内存配置不对Camera HAL和驱动不匹配查dmesg检查ion分配先做V4L2测试SystemUI不停停止资源ID冲突或framework改动不完全清理out目录整编一次确认不会有旧id残留触摸屏方向不对内核input设备上报坐标轴跟LCD方向不一致修改驱动坐标映射或调整.fb/panel旋转参数实际踩坑过程中最有用的一个工具组合是serial console adb logcat dmesg。如果设备起不来先接串口看kernel log是否到了init阶段能进init但SystemServer崩溃用adb logcat抓Java层异常纯驱动问题则dmesg里会直接反映出来。Android源码里很多问题都能通过全量编译前做一次make clean解决不要只在增量编译上打转错误往往来自过期的中间文件。还有一个容易被忽略的细节当你修改framework层Java代码后仅re-build framework.jar未必能保证镜像生效。在Android 5.1里framework相关改动通常需要重新生成system.img必要时还需要同步更新boot.img中的ramdisk因为系统服务的Binder服务描述符或权限配置可能已经变化。我在早期开发时多次遇到“改了代码但手机没有变化”的情况最后发现是只编了单个模块没生成全镜像导致的。另一个操作习惯是从5.1开始有效至今的每次修改AOSP源码前最好创建单独的git分支或至少用git stash保存原始状态。AOSP每个项目本身就是git仓库利用repo工具可以方便地diff改动内容这也方便后续做系统升级时合并代码。别一直改在同一个裸分支上代码搞乱了很难回退。我个人实际操作中的体会是Android 5.1源码的学习路线要比高版本顺畅很多——它不像Android 10以后动辄需要面对HIDL、AIDL接口分区、Treble结构之类的复杂性5.1的结构还保留着相对直接的模块边界你改一个功能能顺着一个线索从头摸到尾。推荐想深入研究Android系统的人从这一版本入手把经典流程过一遍之后再去看高版本会轻松很多。最后再分享一个小技巧编译前先把out目录里上个版本的残留产物清理干净同时确认一下ccache的命中率如果发现命中率突然很低通常是源码路径发生了变化这是正常的。构建工具链和源码版本匹配这件事在Android 5.1时代还很“感性”很多问题看着是编译错误实际是环境和版本的拧巴。遇到报错先别急多看几行上下文很多答案就藏在编译器告诉你的那个文件路径里。本文还有配套的精品资源点击获取
返回列表