Android进阶面试题

android 面试题 - 深入了解

2019-06-03  本文已影响80人  前行的乌龟

ps :持续更新ing...

目前涉及内容:


AIDL & Binder

AIDL 是android 特有的 IPC 跨进程通信机制

这里先了解2个概念:

ok 先有点打底,就怕面试时问概念答不上来。AIDL 采用经典的 C/S 架构(客户端,服务端),其底层原理就是采用的 android 自己的跨进程通信 Binder 来做的

AIDL 自动生成的接口文件中 Stub 这个内部类继承自 Binder,Proxy 远程代理帮我们实现了使用 Binder 来传递获取数据的操作

AIDL 的基础部分看这里:AIDL 从入门到精通

Android 是基于 Linux 内核的,但是 android 即使没用 Linux 自带的诸如:

管道 Pipe,信号 Signal,跟踪 Trace,插口 Socket,消息队列 Message,共享内存 Share Memory,信号量Semaphore

来实现 IPC,非要自己做一套也就是 Binder 出来,自然是有其原因的,所以对于 Binder 我们是需要清楚的,面试大公司会问的,除了面试外也非常有注意我们理解 android 的全貌,对 android 的系统运行有个清晰的认识

进程间通信考虑3个因素:

好了再次介绍概念:

Binder 实现原理

Binder 的实现原理不能不提2个概念:用户空间 | 内核空间,大家还记得 AIDL 中的那个 Binder 桥吗,Binder 就是运行在内核空间的,内核空间的特性就是对所有的进程开放,Linux 大多数 IPC 手段都是通过这个 内核空间 实现的,用 内核空间 做数据中转

为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间(User Space)和内核空间(Kernel Space)。针对 Linux 操作系统而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各进程使用,称为用户空间


所以跨进程通信必然需要有个依托,介于内核空间的特性,Binder 当仁不让的就运行在内核空间里了

Binder 是典型的 C/S 架构,在这个架构中有4个角色:

ServiceManager 大家可能对他干什么的不是很熟悉,ServiceManager 是管理所有的 Service, Service 的启动,生命周期管理,注册都是由 ServiceManager 完成的,我们 startService 即使请求的 ServiceManager 的相关方法。我们可以 Binder 的过程看成网络请求

通常我们访问一个网页的步骤是这样的:首先在浏览器输入一个地址,如 http://www.google.com 然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问 DNS 域名服务器,域名服务器中保存了 http://www.google.com 对应的 ip 地址 10.249.23.13,然后通过这个 ip 地址才能放到到 http://www.google.com 对应的服务器。

Service 在启动时对把自己注册到 ServiceManager 中,以 key/value 的是方式,key 是一个字符串,也是该 AIDL 接口的名字,value 就是这个 Binder 实体在内核空间的地址,然后 ServiceManager 把 Service 端 Binder 的内核引用传递给任意多个 Client 端进程,然后 Client 端借助 Binder 与内核空间通信传递数据以实现跨进程通信,Binder 的核心就是传递 Service 端进程内存中 Binder 对象对应的内核空间中的地址引用


这是最通俗的描述,其实说"Binder 对象对应的内核空间中的地址引用"并不是十分准备的,为了方便理解可以这么说,但是我们大家还可以再深入一点来看看

Linux 下的传统 IPC 通信原理

通常的做法是消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copy_from_user() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输,我们称完成了一次进程间通信。如下图:



这种传统的 IPC 通信方式有两个问题:

性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间

Binder 跨进程通信原理

Binder 并不是 Linux 自己的 IPC 手段,是 Google 自己实现的,是不能同原生 IPC 方案那样直接操作内核空间的,Binder 直接的是内存映射这个概念

得益于 Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)机制,模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信

而这个内核模块就是 Binder 了,Binder 为什么就驱动,是因为 Binder 的运行机制像和硬件通信一样罢了,区别是 Binder 不涉及硬件

一次典型的 Binder 通信是这样的:

还有一点 Service 端的 Binder 是运行在线程池中的,为啥呢,因为一个 Service 进程是要求可以同时与多个 client 进程通信的,这样的话不上线程池怎么能跑得起来

Android 自己的系统服务都是独立运行在各自的进程中的,比如 ServiceManager,ActivityManageService,surafceFiler 都是如此的,他们之中是如何管理的,其实和普通的 Service 一样,这些系统系统服务都是以 Service 的形式存在对外提供服务,那么自然就受 ServiceManager 管制,ServiceManager 是 android 系统启动时第一个启动的系统服务,然后再由 ServiceManager 启动其他的系统服务,系统服务会以自己的类名注册到 ServiceManager 中

但是 ServiceManager 一样也是运行在自己的进程中的,那么最初的 Binder 是怎么通信的,ServiceManager 的 Binder 对应的内核空间引用是固定的 = 0,相当于一个固定的 IP 地址,所以能够与其他进程直接通信,从而简历最初的 Binder 通道出来

其实 Binder 对象可以为方法参数可以在进程间传递,AIDL 的双向通信就是在建立 Client 和 service 的通信后,Client 调用 service 的方法把 Client 端的 Binder 对象传给 service,这样双方各自拥有对方的 Binder,就可以双向通信了

好了基本上就是这样了,更详细的原理看下面2篇吧


上一篇下一篇

猜你喜欢

热点阅读