引言
个性化需求在工作和生活中无处不在,比如:
- 我:“我想买一杯奶茶!” 服务员:“您好!先生!请问你是要A大杯 B中杯 C小杯 ?A多糖 B正常 C少糖?”
- 产品:“这个应用不上这个项目!”
- 策划:“这个菜单在这个机型上去掉!”
- 法务:“这个国家不允许有通话录音功能!”
在Android系统开发时,同样会遇到各种各样的定制需求,有来自客户的,有来自产品的,当然也有来自我们开发者本身。那么在个性化定制需求无处不在的背景下,必须得有一套灵活的资源个性化机制来应对海量的需求,否则将会面临开发,测试和软件维护的成本指数上升的风险。
开发成本:每次来一个需求都要去找修改点,修改验证,没有形成定制规范;
测试成本:每个不同需求都可能是一个单独的版本,测试范围没形成收敛;
维护成本:个性化需求越多,客户需求越多,软件的版本数量也随之增多,这些软件版本的维护成本激增;
针对这个问题,Android开发项目(AOSP)、Google都推出一些较为先进的方案,一些大型手机厂商也会自主研发定制框架,以满足其特定需求和市场定位。特别在政企和全球运营商市场上,自研的个性化定制方案能够为手机厂商带来降本增效的收益。
接下来介绍下目前几个个性化定制方案:
- AOSP国际化多语言
- Google ADCP
- Google Apex热更新
AOSP 国际化多语言
参考:resources :资源是指代码使用的附加文件和静态内容,例如位图、布局定义、界面字符串、动画说明等。
请始终将应用资源(例如图片和字符串)与代码分隔开,以便能够独立地维护这些资源。此外,还应为特定设备配置提供备用资源,方法是将其进行分组并放入专门命名的资源目录中。在运行时,Android 会根据当前配置使用合适的资源。例如,您可能想根据屏幕尺寸提供不同的界面布局,或根据语言设置提供不同的字符串。
外部化应用资源后,您便可使用在项目的 R 类中生成的资源 ID 来访问相应资源。本文档介绍了如何对 Android 项目中的资源进行分组。此外,还介绍了如何为特定设备配置提供备用资源,以及如何从您的应用代码或其他 XML 文件访问这些资源。
以下是一个简单项目的文件层次结构:
MyProject/
src/
MyActivity.java
res/
drawable/
graphic.png
layout/
main.xml
info.xml
mipmap/
icon.png
values/
strings.xml
下表按优先级顺序列出了有效的配置限定符您可以通过使用短划线分隔每个限定符,从而向一个目录名称添加多个限定符。如果对资源目录使用多个限定符,则必须按照表中所列顺序将相应限定符添加到目录名称中。
实现原理:将所有个性化资源通过目录名+维度区分,在资源索引ResourceTypes中根据当前用户设置的实际纬度(国家,语言,分辨率等)返回正确的资源id,最终在APP层显示。
资源索引具体实现路径可见:frameworks/base/libs/androidfw/ResourceTypes.cpp
int ResTable_config::diff(const ResTable_config& o) const {
int diffs = 0;
if (mcc != o.mcc) diffs |= CONFIG_MCC;
if (mnc != o.mnc) diffs |= CONFIG_MNC;
if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION;
if (density != o.density) diffs |= CONFIG_DENSITY;
if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN;
if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0)
diffs |= CONFIG_KEYBOARD_HIDDEN;
if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD;
if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
if (version != o.version) diffs |= CONFIG_VERSION;
if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR;
if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT;
if ((screenLayout2 & MASK_SCREENROUND) != (o.screenLayout2 & MASK_SCREENROUND)) diffs |= CONFIG_SCREEN_ROUND;
if ((colorMode & MASK_WIDE_COLOR_GAMUT) != (o.colorMode & MASK_WIDE_COLOR_GAMUT)) diffs |= CONFIG_COLOR_MODE;
if ((colorMode & MASK_HDR) != (o.colorMode & MASK_HDR)) diffs |= CONFIG_COLOR_MODE;
if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
const int diff = compareLocales(*this, o);
if (diff) diffs |= CONFIG_LOCALE;
return diffs;
}
小结: AOSP对国际化多语言等个性化支持的实现方式上看出,要实现灵活的个性化定制框架,需要具备以下三个必要条件:
- 资源和代码分离;
- 不同资源以目录+纬度划分放置;
- 资源打包并和主软件包分离(详细可以继续分析下RRO动态资源加载和SRO静态资源加载);
Google ADCP解决方案
1. 什么是ADCP
提到ADCP(Android Device Config门户),就不能不提其中的PAI(Play Auto Installs),利用PAI,OEM可以为设备创建单个软件版本(单个工厂ROM)并支持服务器端配置。然后,制造商可以通过选择要安装的Google Play商店应用来进一步定制该设备,以作为最终用户开箱即用体验的一部分。
简单来说,PAI是利用预置的GMS套件,这些套件APP(主要是Google Play和GMS Core)通过请求ADCP后台识别到设备的唯一信息后,拉取后台配置的各种定制资源(比如APK,配置文件,图片,视频,属性等),是一种线上定制解决方案。
架构图如下:
2. ADCP能配置什么内容
- 通过PAI可以配置Google Plays上的应用,运营商应用和OEM厂商自己的应用;
- Client ID,可以理解为设备唯一标识,说白是用于计费分成用的;
- 连接参数,蓝牙,vowifi,vovideo,nfc等等配置参数;
- 应用配置参数,比如邮件,浏览器默认书签等;
- KV值;
- 其他二进制资源,比如rro,壁纸,铃声等;
小结: ADCP是Google在Android上推出的线上定制(配置)方案,由于是闭源的原因,具体实现就不做深入研究。但可以得出的是线上定制对比本地定制,灵活度更高,适用在定制需求有定期变更的场景。其实现思路是:
- 资源和代码分离;
- 不同资源以目录+纬度划分放置;
- 定制资源包(ADCP可定制内容)和主软件包分离;
Google APEX解决方案
1. 什么是APEX
Android Pony EXpress (APEX) 是 Android 10 中引入的一种容器格式,用于较低级别系统模块的安装流程中。 此格式可帮助更新不适用于标准 Android 应用模型的系统组件。一些示例组件包括原生服务和原生库、硬件抽象层 (HAL))、运行时 (ART) 以及类库。“APEX”这一术语也可以指 APEX 文件。
2. APEX的背景
虽然 Android 支持通过软件包安装程序应用(例如 Google Play 商店应用)更新适用于标准应用模型(如服务、Activity)的模块,但是对于较低级别的操作系统组件,使用类似模型具有以下缺陷:
- 基于 APK 的模块不能在启动序列早期使用。软件包管理器是应用相关信息的中央代码库,只能从 activity 管理器(在启动过程的后期阶段准备就绪)启动。
- APK 格式(特别是清单)专用于 Android 应用,系统模块并不总是适用。
3. APEX格式和内容
从APEX文件的内容中可以看出,通过APEX可以升级或者修复:
- 分区内容:通过Loop Device方式动态挂分区,替换覆盖原有分区内容;
- 可执行程序:比如bin/myservice;
- C库:比如lib/libFoo.so;
- Java库:比如javalib/bar.jar;
小结: APEX方案是Google在Android10后推出的热修复方案,初衷应该是在OTA前的小更新,也可以理解为一种线上定制方案。其实现思路同样是:
- 资源和代码分离;
- 不同资源以目录+纬度划分放置;
- 定制资源包(APEX)和主软件包分离;