为了账号安全,请及时绑定邮箱和手机立即绑定

“魔改”的Android系统:厂商定制的Android系统为什么也要解耦?

标签:
嵌入式

你好,我是黄俊彬。

上节课,我们了解了Android系统开发的基础框架、编译环境、开发工具等基础知识。这节课,让我们聚焦在架构设计层面,看看定制系统里最容易出现哪些架构耦合问题,这些问题又会给整机产品埋下哪些隐患。

想要分析架构层面的耦合问题,自然要先弄清楚规范的Android系统架构长什么样,就让我们从这里开始今天的内容吧!

Android系统架构

做过Android系统定制开发的同学,一定会接触到AOSP。AOSP,全称是Android Open Source Project,中文译为“Android 开放源代码项目”。厂商每年会基于Google开放的最新代码进行适配定制,开发属于自己的OS版本。

首先,我们根据Android的架构图来看看Android系统架构的设计。

对照架构图,我们从上到下来看。在应用框架层上面应该还有一层,就是诸多的应用。

这些应用可以分为2类:一类是系统应用,拥有高的系统权限,可以调用系统提供的高权限接口,例如打电话、短信、设置等应用;另外一类就是非系统应用,与第三方应用一样,例如定制一些便签、运动健康、视频播放等应用。

接下来的第一层就是应用框架层,**应用框架最常被应用开发者使用,对应用提供标准的API来调用系统的能力,从而实现相关的业务功能。**我们在代码编译时,通常会依赖Android SDK的android.jar空包,保证能通过编译。但需要注意的是android.jar具体的实现都在框架层中,实际运行时调用的都是系统中的类。

第二层是Binder IPC。有了 **Binder 进程间通信 (IPC) 机制,应用框架就能跨越进程边界并调用 Android 系统服务代码。**由于系统的很多服务都是运行在System Server进程,但是集成到应用的SDK代码是运行在应用的进程,所以需要通过Binder的方式来实现跨进程间的通信。

第三层是系统服务层。系统服务专注于特定功能的模块化组件,例如窗口管理器、搜索服务或通知管理器。例如我们熟悉的AMS、WMS、PMS等,都运行在系统服务层。

第四层是硬件抽象层 (HAL)。Google 在Android 8.0 里一个名为“Treble”的项目中设计了 HAL层,目的是让制造商能够以更低成本、更轻松快速地将设备更新到新版 Android 系统。在这种新架构中,HAL 接口定义语言指定了 HAL 和其用户之间的接口,让用户无需重新构建 HAL,就能替换 Android 框架

最后一层是Linux内核层。Google在官网介绍的开发Android设备驱动程序与开发典型的 Linux 设备驱动程序类似。但Android 使用的 Linux 内核版本包含一些特殊的补充功能,例如低内存终止守护进程、唤醒锁定、Binder IPC 驱动程序等,对于移动嵌入式平台,这些是非常重要的功能。

对Anroid系统的“魔改”

既然Android系统已经有了规范的架构设计,为什么定制Android系统还会产生耦合的问题呢?

由于手机产品涉及软硬结合,所以一般会采用 IPD 产品开发流程,研发一款手机的时间通常需要3 - 12个月的时间,并按照每款手机项目单独立项跟踪。迫于交付压力,再加上缺少有效的架构设计及守护等问题,开发人员会对系统做各种各样“花式的魔改”,最典型的是后面这3种耦合场景。

场景1:应用之间的耦合

理论上来说,应用之间都是相对独立的。但是在定制系统中,有一些应用在运行时存在相互依赖,例如桌面与负一屏(基于桌面向右滑动后的快捷入口)。这里应用A在运行时可能需要调用应用B提供的某些方法,才能保证功能正常运行,如下图所示。

这里看起来似乎合理,编译上没有依赖,运行时也是通过标准的API调用。但关键的问题是不同项目上的功能有差异,依赖的API会有变化,并且应用之间并没有做好兼容性的处理,这样导致应用B不存在时,应用A无法正常运行。你可以结合下图来理解。

场景2:应用与框架之间的耦合

接下来,我们来看第二种典型的耦合场景,应用与框架之间的耦合。

做过应用开发的同学应该知道,我们需要依赖Android的SDK来开发。因为Google会保持SDK接口的稳定及兼容,所以基于标准SDK开发的应用,才能运行在各个大版本的Android系统中。

但是在框架里面还有一些类被标识了@hide,或者有些类属于com.android.internal中的类,这些都是标准的SDK不会提供的。

但是,厂商可以编译生成完整的android.jar包,这样应用就可以调用到这些非公开的接口,以便实现更加丰富的功能。当然还有一些应用采用另外一种方法,就是使用反射的形式。你可以结合后面的示意图来理解。

由于这部分API,Google在大版本的迭代中并不一定保证兼容,所以这也意味着一旦使用这个特殊的Jar包的应用,就与特定的大版本绑定了。应用需要针对每一个大版本都维护一个特定的APK。

我还遇到过一种更“反直觉”的操作——框架依赖应用。比如在框架层增加代码,会去调用APP中相关的资源及代码。这种反向的耦合更容易出现问题,因为一旦底层框架代码的兼容性没有处理好,就很容易导致无法开机、黑屏等严重后果。

###场景3:框架之间的耦合

第三种典型的耦合场景是框架之间的耦合,这里的框架耦合指的是厂商扩展的代码与框架之间的耦合

为了扩展系统的功能,定制Android系统可以在框架中添加一些代码,例如可以在AMS里面的Activity生命周期回调增加一些统计代码,就能统计到应用界面的一些执行情况。这些能力是三方应用无法实现的功能,是厂商定制应用的优势。你可以参考后面的示意图来理解。

但是缺少规范化的管理及灵活的插桩设计,也会产生耦合问题。我们都知道Google每年都会更新AOSP基线代码,框架之间的耦合会导致扩展的代码与框架代码强关联。一方面这些代码只能跟随着框架代码一起维护,无法做到独立维护;另外一方面当代码有更新时,维护成本也非常高。

耦合带来的问题

除了前面提到的定制Android系统的耦合问题,耦合也会影响到团队效率以及产品质量,接下来我们就重点探讨三个常见问题。

问题1:大量重复的代码合并工作

前面提到Google每年都会升级一个Android的大版本,对于厂商来说,他们其实拿到的是第三手代码。前面还有一个上游——芯片平台。为了帮助你理解,我画了后面这张示意图。

因为是第三手代码,为了保证本地代码能及时同步上游的最新代码,厂商需要定期去同步上游的代码,大版本可能是每年一次,补丁Patch可能是2周一次。由于侵入性的修改,容易导致代码冲突的出现,特别是每次的大版本更新。

另外,由于各种耦合的问题,通常最后量产版本时需要拉去独立的MP分支。这样,并行的项目越多时,合并代码的工作量就会呈指数级爆发。

问题2:并行维护多个版本

由于应用于架构和耦合问题,会让不同项目集成难度升高。因为应用无法做到一个apk适配多个项目,这样对于应用来说往往需要同时维护3-5个版本,并且通常也是采用拉取分支的形式,一个分支出一个项目版本的APK。


同时,维护多个版本带来了大量重复性的工作。例如当修复一个Bug时,需要同步到若干个分支中,并且带给测试同学的压力也非常大。由于每个分支的代码都不是完全一致的,需要做回归测试时,工作量也会翻倍。

问题3:“未知”的产品质量

由于代码的耦合问题,非常容易导致修改代码出现连带问题。所以开发同学会选择尽可能少修改代码,更别谈去做一些中大型的代码重构。在机型数量越来越多的状态下,技术复杂度越来越高。两种压力的共同作用下,代码修改越多,代码重构就变得越来越难,代码质量完全无法把控。

另外,对于产品的质量也带来了非常大的挑战。前面提到的多项目、多版本的问题,导致在最后集成阶段需要大量的回归测试,然而在缺乏高质量的自动化测试覆盖下,仅靠人工很难进行全面的验证,这样就非常容易导致问题流到线上用户手中。

总结

今天我们一起了解了Android的系统架构,与前面讲过的应用分层架构类似,Android系统也是采用模块化的分层架构。各个分层职责清晰,从上到下依次为应用框架层、Binder IPC、系统服务层、硬件抽象层以及Linux内核层。

但是当厂商开始定制Android系统时,由于项目管理、交付压力等等情况,非常容易出现不按规矩出牌的情况,我总结了3种最常见的耦合场景,你可以通过后面的表格回顾。


由于耦合的问题,团队需要完成大量重复、机械性的代码合并工作,也需要同时维护多个并行的版本。开发同学淹没在数不尽的分支合并任务里,测试同学淹没在数不尽的黑盒测试中,团队无法把精力投入到代码优化和更多产品质量优化工作上,时间一久,就会给系统埋下诸多隐患。

下节课,我们就聊聊应对这些耦合问题的具体思路,敬请期待。

思考题

感谢你学完了今天的内容,今天的思考题是这样的:你觉得Android系统的耦合与应用的耦合有哪些相同点和差异点呢?

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消