Client/Server架构,也就是大家最常听到的C/S架构在后端开发中可以说是祖师爷级别的概念了。其实安卓的系统服务的设计也是遵循C/S架构的,甚至和最近很流行的微服务micro service架构也有关联。
今天我想用通俗一点的语言稍微讲解一下安卓端的CS架构,每个组件设计的理念,让大家更清晰的理解一下安卓的客户端和服务器到底是个什么东西。
简单的说,安卓的APP应用层可以当做是安卓C/S架构中的客户端。而安卓系统中,运行在系统进程system process的系统服务system service,被认为是服务器端。
客户端除了app本身之外,还包括安卓SDK的客户端公有API (public API)。基本上sdk里面带Manager字样的类,与之相对应的都会有一个安卓服务器端的实现。我们先抛开实现细节,教教大家怎么寻找客户端对应的服务器实现。
比如可以让app在运行时获取用户手机SIM卡等相关信息的TelephonyManager,大家可以看到它的其中和一个方法,用来获取手机当前的位置的方法。
public CellLocation getCellLocation() {
try {
Bundle bundle = getITelephony().getCellLocation();
return CellLocation.newFromBundle(bundle);
}
catch (RemoteException ex) {
return null;
}
它的调用了getITelehony()来获取一个ITelephony对象,大家一看这种带I开头的类就知道肯定要涉及IPC,跨进程调用了,它的服务器实现必然会继承ITelephony类的Stub。
所以在搜索该Manager的服务器端的实现的时候,直接搜索extends ITelephony的类就好。
public class PhoneInterfaceManager extends ITelephony.Stub {
@Override public CellIdentity getCellLocation(String callingPackage, String callingFeatureId) {
.....
}
}
果不其然,整个framework就只有PhoneInterfaceManager 继承了该ITelephony类的Stub,并且也可以找打之前getCellLocation 的具体实现。
当然其他的各种manager也是照着这样的方式一一实现的。整个安卓的客户端public API都有与之对应的实现,都是实现以I开头的Stub类。
这个小节我先简单的教大家怎么找到manager类的对应服务器端的实现,至于安卓是具体怎么实现这样的一对一映射我们接下来的小节再详细说明。
大家都知道编译一个安卓app需要android sdk,但是不少人没搞明白这个SDK到底是编译时用的还是运行时用的。
解答这个问题其实很简单,大家打开你的android studio,随便点击一个manager类的源码,就可以看出来。
很明显,这些SDK里面的类都不会最终打包进app的APK文件里面,如果真的用了这些类当做实现,运行时肯定都会crash(毕竟这里面全都是抛RuntimeException)。
所以SDK的类,都是没有具体实现的,我们把它们叫Stub类。他们只是用来帮助我们进行编译而已,目的是让app开发者能成功打包编译出APK。这个和gradle里面的compileOnly 关键字很像。而且这样设计也很有道理,毕竟如果每个app都需要把android framework的公有api类都打包进APK,那每个app安装文件都将变得巨大无比。
dependencies {
compileOnly 'javax.servlet:servlet-api:2.5'
}
但是在之前的图里,我明明还是把Manager类画在了APP的进程里面,也就是说运行时我们还是需要Manager类的。那这些类到底从哪来的呢?
关于Zygote的细节我们就不聊那么多,网上资源挺多的。但是我们需要知道的重点就是,每一个APP都是Zygote复制出来的一个进程(具体的实现应该是linux里面的fork),这个进程Dalvik(或者是ART)都是一个独立的虚拟机,virtual machine。在最原始的进程里面,所有的public API, 也就是那些客户端的Manager类都已经被加载进该进程(虚拟机)了。所以当每个app被启动的时候,启动APP的dalvik或者ART已经加载过了所有的客户端API的类。这样,app在运行时就不会找不到对应的Manager类 (symbol not found),但是同时通过android的stub sdk又减少了apk大小,可以说是一举两得。
https://man7.org/linux/man-pages/man2/fork.2.html
最后关于客户端再分享一个小知识,就是关于隐藏API。安卓的很多public manager class会有自己的隐藏api,可以看源码有没有带@hide的字样。比如AudioManager的getMasterStreamType方法,注释里面就加了hide。
/** *
Get the stream type whose volume is driving the UI sounds volume.
UI sounds are screen lock/unlock, camera shutter, key clicks...
@hide
*/
public int getMasterStreamType() {
}
加了这个注释之后,安卓在编译Stub sdk 的时候,就会把带这个注释的方法从stub类里面删掉,这样导致app开发者在编译阶段找不到该方法,从而达到隐藏的效果。大家通过android studio打开AudioManager的stub 就可以发现。
所以该隐藏也只是编译阶段隐藏而已,并没有做到真正的运行时隐藏。
也正因为该隐藏方式是在客户端的编译阶段隐藏,而且在运行时的dalvik和art虚拟机里面还是有对应类被加载,所以在运行时App还是可以通过java的反射来进行访问。。。。安全性并没有那么高。
有一些安全性要求比较高的API,会强制在服务器端run time检查调用者的进程ID,严格控制调用者身份。比如ActivityManager里面的一个方法,会检查callingUId。
void enforceShellRestriction(String restriction, int userHandle)
{
if (Binder.getCallingUid() == Process.SHELL_UID)
{ if (userHandle < 0 || mUserManager.hasUserRestriction(restriction, userHandle))
{ throw new SecurityException("Shell does not have permission to access user " + userHandle);
}
} }
这个思想其实和真正的后端开发就很相似了,你想在客户端做安全性验证,怎么也会出现漏洞。比如游戏客户端可以用很多种方式绕过安全检查,或者甚至修改游戏端运行时代码等等方式作弊。
说了这么多客户端的实现。是时候聊聊服务器端的实现了。服务器端的具体实现,我推荐大家看看这篇文章 如何实现一个 System Services?,这是博主吴小龙写的,步骤非常详细,大家可以先过一遍有个印象。
https://juejin.cn/post/6961760668705882142
不过再多的细节,服务器端的实现绕不过这三个概念:1. AIDL 2. Bound Service 3. ServiceManager and SystemService
大部分朋友应该对AIDL怎么用已经很了解了,这次我就不讲具体怎么用,而是应该怎么理解AIDL的设计初衷。
其实我们站在开发者的角度就可以很容易理解。当我的APP需要进行跨进程通信, 我作为开发者肯定希望有一个方便的客户端API,可以直接调用该API就间接的和system process通信.而不需要写一些非常复杂的C++的代码,比如我们总不能要求app开发者写jni文件去调用linux的系统调用来做这个事情。
站在服务器,也就是系统开发者的角度,我肯定希望当客户端调用我的某一个方法的时候,我不需要做额外的数据的序列化和反序列化。或者通俗一点说,就是客户端的调用的API的方法名,和我服务器端的方法名一致,接口长的样子要一样。比如在客户端TelephonyManager#isEmergencyNumber调用了ITelephony#isEmergencyNumber, 那么服务器端我也希望被调用服务器端实现的isEmergencyNumber方法。
AIDL机制就很好的解决了这个问题。AIDL可以帮助客户端和系统服务端开发者自动生成代码,包装了一些真正的跨进程通信的实现,让开发者不需要自己写linux的系统调用,同时还可以保持双方接口的一致(方法名一样等等)。
所以这一套设计方案说到底,就是为了开发者,尤其是app开发者更方便的开发而已。它的出现让跨进程变得更方便,但是这个跨进程也是在现有框架下进行,比如,客户端app开发要想和系统进行通信,必须通过系统现有的service与manager API进行通信,也还是有一定的限制。
实现了一个系统级别的服务,总得先生成该服务的实例,并且保存在某处(确保不会被垃圾回收),才能保证客户端能正常的调用。
安卓的ServiceManager就提供了这样的功能。
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
{
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
}
catch (RemoteException e) { Log.e(TAG, "error in addService", e); }
}
这个方法最后会调用一个C++的底层代码,把服务实例注册。当前端的Manager api调用ServiceStub的方法的时候,安卓系统会通过IPC调用这些注册了的Service实例。
服务器端的服务可以以Service的的形式存在,也可以以SystemService的形式存在。Service就不用多说了,大家都知道怎么做bind service。
SystemService就不太一样了,大家可以看看这个类是个啥。
@SystemApi(client = Client.SYSTEM_SERVER)
public abstract class SystemService {
public void onBootPhase(@BootPhase int phase) {}
}
这个抽象类并不以Service的形式存在,而是一个简单的类。它唯一不一样的是提供了很多和启动周期相关的回调(boot phase).很多朋友不太懂什么是启动周期,其实很好理解,通俗一点就是,安卓设备被设置了屏幕锁之后,设备启动之后会被分成不同的启动阶段。
比如设备启动但是没有解锁是一个阶段,设备被解锁之后是另一个阶段。每个阶段开始之后,安卓的system server会启动不同的进程。最明显的例子是打电话的进程,设备在没有解锁之前也需要能接收或者拨打紧急电话。
SystemService类暴露了这样一个回调就用意很明显了,这样可以让不同的系统服务在不同的启动阶段做不同的事情。
比如安卓监控设备温度的系统服务,他只有在ActivityManager被加载之后才进行一系列业务逻辑的执行。
class ThermalManagerService extends SystemService {
@Override public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
onActivityManagerReady();
}
}
}
那么有了这么多服务,在哪里开始执行呢。就在SystemServer里面。
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/power/ThermalManagerService.java;drc=b0193ccac5b8399f9b5ef270d102b5a50f9446ab;l=72
SystemServer.java的核心代码在这几行:
try {
t.traceBegin("StartServices");
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
startApexServices(t);
}
BootStrap service就是指我们上一节说到的SystemService,这些对象和bootstrap也就是启动周期紧密的相关,所以都是SystemService。Core Service就是系统需要的但是对bootstrap不敏感的服务。但是无论是哪种service最后都毫无例外的将自己注册在ServiceManager了。
ServiceManager.addService("sec_key_att_app_id_provider", new KeyAttestationApplicationIdProviderService(context));
就这样,安卓系统成功的创建了所有需要的服务的实例,并且将其注册了起来。到此为止,客户端已经可以尝试通过Manager API对这些系统服务进行IPC通信了。而且所有的这些Service的实例都被保存在一个List里面。
class SystemServiceManager {
private List<SystemService> mServices;
}
所以并不用担心这些Service被垃圾回收。到这里基本上安卓的前端和后端我们就都了解了。但是这些都只是一些概念的皮毛,有很多细节还没覆盖到。大家有兴趣可以再多查查资料。不过,安卓在架构上也在不断的进化。一个例子:很多后端的开发概念,微服务现在在安卓的架构中也开始被支持了。
从安卓10开始,android 的系统开发者已经开始尝试把很多framework中的代码模块化。从framework/base移动到packages/modules/下面,变成独立的模块,从而将该模块编译成独立的APEX文件(一个类似APK的安装文件)。
最显著的就是蓝牙模块:
https://cs.android.com/android/platform/superproject/+/master:packages/modules/Bluetooth/
这个模块编译之后是一个类似APK的APEX文件,这样的好处是:
蓝牙模块可以自己单独的通过google playstore更新了!!!!
也就是说,以后安卓的某一个模块出了新的fix,用户不需要再下载完整庞大的img了,安卓只需要通过playstore下载对应的APEX文件实现更新!
APEX文件的具体格式可以看这:
https://source.android.com/docs/core/ota/apex
可以说,安卓在C/S的架构上的进化从来没停止过。系统开发者的学习脚步也不能停啊。。。
6月5日,一张券商降薪截图在社交媒体疯传。截图提到,当日上午,某中字头头部券商召开大会,除了MD外全员降薪,且降薪不只是降奖金,而是直接降底薪。按照职级不同,SA1降6K,SA3降8K,VP降8K—10K。据了解,降薪大概率整体属实,但具体幅度有所差异,且不同区域、不同业务条线目前掌握的降薪情况也不尽相同。
今日,蔚来 CEO 李斌在 2023 高通汽车技术与合作峰会上爆料,蔚来第二代技术平台的全系车型已标配第三代骁龙座舱平台。
Meta公司周一(5月22日)推出了一个开源AI语言模型——大规模多语言语音(Massively Multilingual Speech, MMS)模型,可以识别和产生1000多种语言的语音——比目前可用的模型增加了10倍。研究人员表示,他们的模型可以转换1000多种语言,但能识别4000多种语言。
歌手孙燕姿在更新动态中回应了近日引发争议的“顶流AI歌手孙燕姿”,笑称粉丝已经接受她是“冷门”歌手,而AI成为了目前的顶流。
5月31日晚,荣耀方面对澎湃新闻记者表示,上海荣耀智能科技开发有限公司是荣耀位于上海的研究所,是荣耀在中国的5个研究中心之一,重点方向在终端侧核心软件、图形算法、通信、拍照等方面研究开发工作。荣耀强调,坚持以用户为中心,开放创新,与全球合作伙伴一起为用户提供最佳产品解决方案。
据北京市市场监督管理局公示信息,5月24日,苹果电子产品商贸(北京)有限公司因发布虚假广告被北京市东城区市场监督管理局处以20万元的行政处罚。
据外媒5月24日消息,全球最大的个人电脑制造商联想表示,在2023年1-3月期间,该公司裁员了约5%,这是由于PC市场不景气导致的。
日前,有网络博主号称拍摄到了小米首款汽车MS11的高清视频。从视频中可以看出,新车依旧包裹大面积的伪装,据该博主称,他之所以确定这是小米汽车,是因为靠近观察之后,发现它的三角形大灯轮廓和其最初手绘的小米汽车假想图几乎一模一样。
超过 350 名从事人工智能工作的高管、研究人员和工程师签署了这份由非盈利组织人工智能安全中心发布的公开信,认为人工智能具备可能导致人类灭绝的风险,应当将其视为与流行病和核战争同等的社会风险。
日前,以押注“颠覆性创新”著称的ARK Invest创始人Cathie Wood在接受媒体采访时表示,软件提供商将是人工智能狂潮的下一个受益板块。英伟达每卖出1美元的硬件,软件供应商SaaS供应商就会产生8美元的收入。
据报道,阿里巴巴研究员吴翰清已于近期离职,钉钉显示其离职时间是5月19日。在阿里内部,研究员的职级为P10。据消息人士透露,吴翰清离职后,选择AI短视频赛道创业,已经close一轮融资。对于上述消息,截至发稿,阿里尚未回应。
阿里巴巴集团官微宣布,2023年六大业务集团总计需新招15000人,其中校招超过3000人。同时表示,“近日,关于淘宝天猫、阿里云、菜鸟、本地生活各个业务裁员谣言传得很厉害,但谣言就是谣言。我们的招聘正在紧锣密鼓的进行。”
“现今每一个存在的应用都将被AI 2.0重构,我觉得整个AI大模型带来的机遇和技术浪潮,会比过去Windows和安卓大10倍。”李开复表示。
苹果发布Vision Pro头显,正式宣布开启空间计算时代;苹果还发布新款MacBook Air,新款Mac Studio,并展示了iOS17、iPadOS 17、macOS Sonoma和watchOS10等新系统;Vision Pro头显售价3499美元,将于2024年初正式在美国市场发售;华尔街并不看好Vision Pro,苹果股价周一创历史新高后由涨转跌。
5月25日,长城汽车就比亚迪秦PLUS DM-i、宋PLUS DM-i采用常压油箱,涉嫌整车蒸发污染物排放不达标的问题进行举报。
近日,一个名为“贾跃亭”的抖音账号悄然出现,带有“FF创始人、合伙人、首席产品及用户生态官, LeEco 乐视创始人”等标签,IP 地址显示为美国。
近日,苹果位于天猫的Apple Store官方旗舰店挂出直播预告,表示将在5月31日晚19时开启官方直播,这也是苹果官方在电商平台的全球首次直播。
5月29日消息,继上周远超预期的财报业绩预测引得股价和市值史诗级暴涨后,今日,英伟达(NVIDIA)创始人兼CEO黄仁勋穿着标志性的皮衣,意气风发地出现在台北电脑展COMPUTEX 2023上,在主题演讲期间先是现场给自家显卡带货,然后一连公布涉及加速计算和人工智能(AI)的多项进展。
前京东集团副总裁、京东探索研究院副院长梅涛自今年初离职后,确认在 AI 领域创业,成立生成式 AI 公司 HiDream.ai。