GVKun编程网logo

Android源码的Binder权限是如何控制?全网独家首发!(android binder原理)

22

对于想了解Android源码的Binder权限是如何控制?全网独家首发!的读者,本文将提供新的信息,我们将详细介绍androidbinder原理,并且为您提供关于2021届毕业生还没找到Android

对于想了解Android源码的Binder权限是如何控制?全网独家首发!的读者,本文将提供新的信息,我们将详细介绍android binder原理,并且为您提供关于2021届毕业生还没找到Android开发工作,全网独家首发!、Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?、Android service 起动顺序如何控制?、Android 进阶 ——Android 跨进程通讯机制之 Binder、IBinder、Parcel、AIDL的有价值信息。

本文目录一览:

Android源码的Binder权限是如何控制?全网独家首发!(android binder原理)

Android源码的Binder权限是如何控制?全网独家首发!(android binder原理)

这篇文章主要介绍了20道经典Handler题及答案解析,文中通过示例代码介绍的非常详细,对大家的学习或者面试复习具有一定的参考学习价值,内容过多可以先收藏慢慢观看,需要的朋友们下面随着小编来一起学习学习吧。

另外各位观众老爷觉得内容还可以的,请给个赞和评论,你的点赞和评论是我最大的动力,谢谢!

在很多程序员看来,数据结构,算法这一类的东西感觉没用,在实践中都不常用,所以都会很忽视这类内容,但是在很多公司看来,尤其是大公司看来数据结构和算法这种东西确实最有用,而且经常在笔试和面试中出现。为什么会这样呢?

1、看似最枯燥、最基础的东西往往具有最长久的生命力

像数据结构,算法这类东西,还有有一些计算机原理之类的知识,这些东西都是编程和实践的根本。他们看似枯燥和基础,但是具有最长久的生命力。

我知道,你可能熟悉各种框架,各种开源库的使用,但是那又怎样?看着各种框架和开源库的文档,只要有编程基础,谁不会照着葫芦画瓢呢?

不要天天谈什么框架,什么库,框架每年层出不穷,可是扒下框架那层炫酷漂亮的外衣,里面还是那些最基础的知识和原理。就是这些算法,数据结构,计算机网络,计算机原理这些看似基础的东西。如果这些掌握扎实了,你才有更加深一步的可能。

编程语言和编程框架等这些表面的东西,对于一个基础扎实的程序员来说,学习起来很快,成长也很快,如果这些基础和原理都懂,你就可能会写出这样的框架来。技术更新迭代快,语言层出不穷,但是数据结构,算法,计算机原理这类的东西确实没有变。

2、数据结构和算法在面试的时候最容易量化和体现能力

什么意思呢?在面试或者笔试的时候,面试数据结构和算法,可以面出你的思维能力,思考能力,这个能力对于编程来说很重要。比如:如果面试你使用过什么框架吗?你说:会,使用过,然后你谈了谈使用这些框架的一些知识和遇到的坑,以及怎么解决的?通过这样的问题,不能看出的思维能力和编程能力,只能看出你确实会用这个东西。

而面试算法和数据结构不一样了,以为面试这种东西可以让你写出来,或者写伪代码,而且这些能力如果你有,你的成长空间也高。面试你编程的实践能力,不可能让你上机去敲一个模块的实现吧?而让你写个算法和数据结构却很方便,也可以量化。

所以,很多人平时不用算法和数据结构,在跳槽的时候,也会提前去复习算法和数据结构的知识,因为这是面试中很常见的问题。但是我还是建议大家能够一直有这种能力。不要因为不常用而忽视这些基本的东西,这些东西才是精华。

最后

说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节

还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一线互联网面试专题

379页的Android进阶知识大全

379页的Android进阶知识大全

点击:

《Android架构视频+BAT面试专题PDF+学习笔记​》

即可免费获取~

docs.qq.com/doc/DSkNLaERkbnFoS0ZF)​》**

即可免费获取~

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021届毕业生还没找到Android开发工作,全网独家首发!

2021届毕业生还没找到Android开发工作,全网独家首发!

前言

今年的面试形势不容乐观,竞争愈发激烈,进大厂的难度又增加了。
但是,梦想还是要有的,万一实现了呢?这不就有一只幸运鹅。

我的一个朋友,几天前,他百年不见更新的朋友圈有了新动态,他居然晒了他的offer,配的文案就是这篇文章的标题:算法太TM重要了!刷完这些题,我终于拿到了梦寐以求的大厂offer!

下面有不少前来贺喜的朋友,就是发言酸溜溜的。

正文

这份资料,反正兜兜转转耗时3个月,在我和我一群朋友的努力以及那个傻子的催促下,终于做出来了。这份高级的闪亮的,耗费我无数心血的面试杀手锏,终于面世了,太不容易了一把辛酸泪!

接下来,给大家说说,为什么一份资料,居然做了3个月,因为这是,我拜托我在美团,腾讯,百度,网易云…的一些朋友和我一起搜集的,因为他们都做过面试官,所以面试的问题肯定是比网上搜集或者面试者们面试口述的题目来的体系一点,这份面试杀手锏,一共十二个板块:

一、Java 基础部分

二、Jvm

三、计算机网络部分

四、Android面试题

五、Android Framework相关

六、性能优化专题

七、算法合集

八、kotlin专题

九、Flutter专题

十、JNI模块专题

十一、Android NDK开发 JNI类型签名和方法签名

十二、JNI实现java与c/c++相互通讯

接下来看看具体内容(由于篇幅问题,这里只展示一部分):

  • 目录篇(部分)

解答篇(部分)


最后

**一个零基础的新人,我认为坚持是最最重要的。**我的很多朋友都找我来学习过,我也很用心的教他们,可是不到一个月就坚持不下来了。我认为他们坚持不下来有两点主要原因:

他们打算入行不是因为兴趣,而是因为所谓的IT行业工资高,或者说完全对未来没有任何规划。

刚开始学的时候确实很枯燥,这确实对你是个考验,所以说坚持下来也很不容易,但是如果你有兴趣就不会认为这是累,不会认为这很枯燥,总之还是贵在坚持。

技术提升遇到瓶颈了?缺高级Android进阶视频学习提升自己吗?还有大量大厂面试题为你面试做准备!

点击:Android 学习,面试文档,视频收集大整理

来获取学习资料提升自己去挑战一下BAT面试难关吧

对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

不论遇到什么困难,都不应该成为我们放弃的理由!

如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!

,其余的都不重要,希望读者们能谨记这一点。

不论遇到什么困难,都不应该成为我们放弃的理由!

如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!

最后祝各位新人都能坚持下来,学有所成。

Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?

Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?


目录:

  • Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?

  • Android : 跟我学Binder --- (2) AIDL分析及手动实现

  • Android : 跟我学Binder --- (3) C程序示例

  • Android : 跟我学Binder --- (4) 驱动情景分析

  • Android : 跟我学Binder --- (5) C++实现

  • Android : 跟我学Binder --- (6)  JAVA实现

 

 

一、引言

  如果把Android系统比作一幅精美绝伦的画,那Binder则是其浓墨重彩的独特一笔。初步了解过的人应该知道Binder是Android核心进程间通信(IPC:Internet Process Connection)手段之一,它是基于开源的 OpenBinder 实现,OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接手。从字面上来解释 Binder 有胶水、粘合剂的意思,顾名思义就是粘和不同的进程,使之实现通信。而日常开发中涉及到的如:AIDL、插件化编程技术 等等,底层通信实现都离不开Binder,所以如果想在“Android系统开发工程师”的头衔前面加上“高级”两个字,那理解、掌握Binder机制则是必经之路。

 

二、Linux 下传统的进程间通信原理

  首先了解一下 Linux IPC 相关的概念和原理有助于理解 Binder 通信原理。

  1.Linux 中跨进程通信涉及到的一些基本概念:

    进程隔离进程间不可直接相互访问资源

      简单的说就是操作系统中,进程与进程间内存是不共享的,两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,这就是进程隔离的通俗解释。A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。

 

    ②进程空间划分:用户空间(User Space)/内核空间(Kernel Space)

      现在操作系统都是采用的虚拟存储器,对于 32 位系统而言,它的寻址空间(虚拟存储空间)就是 2 的 32 次方,也就是 4GB。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间(User Space)和内核空间(Kernel Space)。针对 Linux 操作系统而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各进程使用,称为用户空间,它们在自己的空间运行,相互隔离。

 

    ③系统调用:用户态/内核态

      虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。为了突破隔离限制,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态),执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。当进程在执行用户自己的代码的时候,则称其处于用户运行态(用户态)。系统调用主要通过如下两个函数来实现,在编写Linux设备驱动程序时经常用到:

      copy_from_user() //将数据从用户空间拷贝到内核空间
      copy_to_user() //将数据从内核空间拷贝到用户空间

  2.Linux 下的传统 IPC 通信原理:

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

  但是这种传统的 IPC 通信方式有两个显著缺点:

  (1)一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝,效率低,可能此时会想到共享内存方式,但是其难于控制不稳定;

  (2)接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,对于手机这种资源紧张的嵌入式移动设备来说无疑是巨大负担;

  可见传统IPC效率低,占资源,除此之外还有安全、稳定性等缺点不足以胜任Android的核心进程间通信方式,下面正式介绍Binder机制,看其有何过人之处。

 

三、为何要用Binder通信机制?

  Android系统的内核Linux已经有很多进程间通信的方式,比如:管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)等 IPC 机制,那Android为何还要再实现一个Binder IPC 呢?主要是基于高效性稳定性安全性几方面原因。

  1.高效对于手机移动通信设备来说,是基于Client-Server即C/S架构的通信方式,而上面提到的Linux传统IPC中只有socket支持Client-Server的通信方式,但是socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。

IPC方式 数据拷贝次数
共享内存 0
Binder 1
Socket/管道/消息队列 2

  2.稳定性Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。所以从稳定性的角度讲,Binder 机制是也优于内存共享的复杂控制缺点。

  3.安全Android 作为一个开放性的平台,市场上有各类海量的应用供用户选择安装,因此安全性对于 Android 平台而言极其重要。作为用户当然不希望我们下载的 APP 偷偷读取我的通信录,上传我的隐私数据,后台偷跑流量、消耗手机电量。传统的 IPC 没有任何安全措施,完全依赖上层协议来确保。首先传统的 IPC 接收方无法获得对方可靠的进程用户ID/进程ID(UID/PID),从而无法鉴别对方身份。Android 为每个安装好的 APP 分配了自己的 UID,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC 访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。同时 Binder 既支持实名 Binder,又支持匿名 Binder,安全性高。

    

  Binder优势总结:

优势 描述
性能 只需要一次数据拷贝,性能上仅次于共享内存
稳定性 基于 C/S 架构,职责明确、架构清晰,因此稳定性好
安全性 为每个 APP 分配 UID,进程的 UID 是鉴别进程身份的重要标志

 

 

  另外上面讲过Linux IPC的通信原理,现在正式介绍 Binder IPC 的通信原理

  正如前面所说,跨进程通信是需要内核空间做支持的。传统的 IPC 机制如管道、Socket 都是内核的一部分,因此通过内核支持来实现进程间通信自然是没问题的。但是 Binder 并不是 Linux 系统内核的一部分,那怎么办呢?这就得益于 Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制;模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信,此内核模块就叫 Binder 驱动(Binder Dirver)。

  那么在 Android 系统中用户进程之间是如何通过这个内核模块(Binder 驱动)来实现通信的呢?难道是和前面说的传统 IPC 机制一样,先将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程,通过两次拷贝来实现吗?显然不是,否则也不会有开篇所说的 Binder 在性能方面的优势了。这就不得说到 Linux 下的另一个概念:内存映射(mmap):将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间,内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。

  但是 mmap() 通常是用在有物理介质的文件系统上的,比如对flash的操作,因为进程中的用户区域是不能直接和物理设备打交道的,如果想要把flash上的数据读取到进程的用户区域,需要两次拷贝(flash-->内核空间-->用户空间),通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率。而 Binder 并不存在物理介质,因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。mmap()使用方法如下:

/*fd描述为打开的/dev/binder文件句柄;
 *mapsize为映射内存区域的大小;
 *mapped为映射到用户空间的内存起始地址;
 */
 bs->fd = open("/dev/binder", O_RDWR);
 bs->mapsize = mapsize;
 bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

 一次完整的 Binder IPC 通信过程通常是这样:

  ①首先 Binder 驱动在内核空间创建一个数据接收缓存区;

  ②接着在内核空间开辟一块内核缓存区,建立内核缓存区内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区接收进程用户空间地址的映射关系;

  ③发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间;

 

四、Binder通信模型实现

  通过介绍、对比Android Binder IPC和Linux 传统IPC之间的差异,Binder IPC的各优点已经凸显出来。一次完整的进程间通信必然至少包含两个进程,通常我们称通信的双方分别为客户端进程(Client)和服务端进程(Server),由于进程隔离机制的存在,通信双方必然需要借助 Binder 来实现。接下来看看实现层面是如何设计的?

 1. Client/Server/ServiceManager/驱动

  前面介绍过,Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder 驱动。其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。其中 Service Manager 和 Binder 驱动由系统提供,而 Client、Server 由应用程序来实现。Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。

  Client、Server、ServiceManager、Binder 驱动这几个组件在通信过程中扮演的角色就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder 驱动)之前的关系。通常我们访问一个网页的步骤是这样的:首先在浏览器输入一个地址,如 www.google.com 然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问 DNS 域名服务器,域名服务器中保存了 www.google.com 对应的 ip 地址 10.249.23.13,然后通过这个 ip 地址才能放到到 www.google.com 对应的服务器。下面是Binder类比介绍:

Binder 驱动
  Binder 驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。 

ServiceManager 与实名 Binder
  ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了有 IP 地址外还有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表。
  细心的读者可能会发现,ServierManager 是一个进程,Server 是另一个进程,Server 向 ServiceManager 中注册 Binder 必然涉及到进程间通信。当前实现进程间通信又要用到进程间通信,这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋!Binder 的实现比较巧妙,就是预先创造一只鸡来下蛋,ServiceManager 和其他进程同样采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 实体,其他进程都是 Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用 BINDER_SET_CONTEXT_MGR 命令将自己注册成 ServiceManager 时 Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡)。其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信。类比互联网,0 号引用就好比是域名服务器的地址,你必须预先动态或者手工配置好。要注意的是,这里说的 Client 是相对于 ServiceManager 而言的,一个进程或者应用程序可能是提供服务的 Server,但对于 ServiceManager 来说它仍然是个 Client。

Client 获得实名 Binder 的引用
  Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字获得 Binder 的引用了。Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder: 我申请访问名字叫张三的 Binder 引用。ServiceManager 收到这个请求后从请求数据包中取出 Binder 名称,在查找表里找到对应的条目,取出对应的 Binder 引用作为回复发送给发起请求的 Client。从面向对象的角度看,Server 中的 Binder 实体现在有两个引用:一个位于 ServiceManager 中,一个位于发起请求的 Client 中。如果接下来有更多的 Client 请求该 Binder,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。

至此,大致能总结出 Binder 通信过程:

  ①首先,一个进程使用 BINDER_SET_CONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;

  ②Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。

  ③Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。

 

 2. Binder 通信中的代理模式

  目前已经解释清楚 Client、Server 借助 Binder 驱动完成跨进程通信的实现机制了,但是还有个问题比较困惑:A 进程想要 B 进程中某个对象(object)是如何实现的呢?毕竟它们分属不同的进程,A 进程 没法直接使用 B 进程中的 object。

  前面介绍跨进程通信的过程都有 Binder 驱动的参与,因此在数据流经 Binder 驱动的时候驱动会对数据做一层转换。当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy,这个 objectProxy 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己,当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了。

   到此可以对 Binder 做个更加全面的定义了:

  • 从进程间通信的角度看,Binder 是一种进程间通信的机制;
  • 从 Server 进程的角度看,Binder 指的是 Server 中的 Binder 实体对象;
  • 从 Client 进程的角度看,Binder 指的是对 Binder 代理对象,是 Binder 实体对象的一个远程代理;
  • 从传输过程的角度看,Binder 是一个可以跨进程传输的对象,Binder 驱动会对这个跨越进程边界的对象做一点点特殊处理,自动完成代理对象和本地对象之间的转换。

 

 

  可以参考该博客自测对binder机制的理解程度:https://www.jianshu.com/p/adaa1a39a274

 

-end-

Android service 起动顺序如何控制?

Android service 起动顺序如何控制?


http://zhidao.baidu.com/link?url=IG1bovv4CIrKXdIVPw79GRUESyQpW0YC4KM8RdgURFLNGtBvhW6FGj7NZWavaWrI4D1PR1hfcTXvqcq-ZqPGLa


你说的是很多个服务接受同一个广播启动,然后想让其中的服务接受广播的启动顺序吗?
如果是的话,你在你接受广播的receiver位置增加 android:priority的属性,该属性值越高,
越先收到广播,当你收到广播之后再onReceive里面使用abortBroadcast(),可以中断该广播,后面就接受不到这个广播了;
动态注册的广播接收器没有试过,下面是静态注册广播的例子:
<receiver android:name=".YourReceiverName" android:enabled="true">
    <intent-filter android:priority="20">
          <action android:name="android.provider.Telephony.SMS_RECEIVED" />
   </intent-filter>
</receiver>

Android 进阶 ——Android 跨进程通讯机制之 Binder、IBinder、Parcel、AIDL

Android 进阶 ——Android 跨进程通讯机制之 Binder、IBinder、Parcel、AIDL

前言

Binder 机制是 Android 系统提供的跨进程通讯机制,这篇文章开始会从 Linux 相关的基础概念知识开始介绍,从基础概念知识中引出 Binder 机制,归纳 Binder 机制与 Linux 系统的跨进程机制的优缺点,接着分析 Binder 的通信模型和原理,而 Binder 机制最佳体现就是 AIDL,所以在后面会分析 AIDL 的实现原理,最后简单的提下 AMS 的 Binder 体系,整篇文章中间会穿插有 IBinder、Binder、Parcel 的介绍,整篇文章阅读难度不大,不会涉及到 framework 层的 Binder 原理,AIDL 部分需要有 AIDL 的使用基础

基础概念

基础概念部分介绍 Linux 的某些机制,主要想表达 Binder 驱动的出现的原因,如果对 Linux 熟悉的可以直接跳过这部分,看第五点即可

一、进程隔离

出于安全考虑,一个进程不能操作另一个进程的数据,进而一个操作系统必须具备进程隔离这个特性。在 Linux 系统中,虚拟内存机制为每个进程分配了线性连续的内存空间,操作系统将这种虚拟内存空间映射到物理内存空间,每个进程有自己的虚拟内存空间,进而不能操作其他进程的内存空间,每个进程只能操作自己的虚拟内存空间,只有操作系统才有权限操作物理内存空间。进程隔离保证了每个进程的内存安全,但是在大多数情形下,不同进程间的数据通讯是不可避免的,因此操作系统必须提供跨进程通信机制

二、用户空间和内核空间

  • 用户空间:表示进程运行在一个特定的操作模式中,没有接触物理内存或设备的权限
  • 内核空间:表示独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限

三、系统调用 / 内核态 / 用户态

抽象来看,操作系统中安全边界的概念就像环路的概念一样 (前提是系统支持这种特性),一个环上持有一个特定的权限组,Intel 支持四层环,但是 Linux 只使用了其中的两环 (0 号环持有全部权限,3 号环持有最少权限,1 号和 2 号环未使用),系统进程运行在 1 号环,用户进程运行在3号环,如果一个用户进程需要其他高级权限,其必须从3号环过渡成0号环,过渡需要通过一个安全参数检查的网关,这种过渡被称为系统调用,在执行过程中会产生一定数量的计算开销。所以,用户空间要访问内核空间的唯一方式就是系统调用 (System Call)

这里写图片描述

四、内核模块 / 驱动

通过系统调用,用户空间可以访问内核空间,它是怎么做到访问内核空间的呢?Linux 的动态可加载内核模块机制解决了这个问题,模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。这样,Android 系统可以通过添加一个内核模块运行在内核空间,用户进程之间的通过这个模块作为桥梁,就可以完成通信了。在 Android 系统中,这个运行在内核空间的,负责各个用户进程通过 Binder 通信的内核模块叫做 Binder 驱动

五、简单的总结

将前面的所有概念连接起来理解就会非常好消化知识点:

  1. Linux 的虚拟内存机制导致内存的隔离,进而导致进程隔离
  2. 进程隔离的出现导致对内存的操作被划分为用户空间和内核空间
  3. 用户空间需要跨权限去访问内核空间,必须使用系统调用去实现
  4. 系统调用需要借助内核模块 / 驱动去完成

前三步决定了进程间通讯需要借助内核模块 / 驱动去实现,而 Binder 驱动就是内核模块 / 驱动中用来实现进程间通讯的

为什么要用 Binder

Linux 提供有管道、消息队列、信号量、内存共享、套接字等跨进程方式,那为什么 Android 要选择 Binder 另起炉灶呢?

一、传输性能好

  • Socket:是一个通用接口,导致其传输效率低,开销大
  • 共享内存:虽然在传输时不需要拷贝数据,但其控制机制复杂
  • Binder:复杂数据类型传递可以复用内存,需要拷贝 1 次数据
  • 管道和消息队列:采用存储转发方式,至少需要拷贝 2 次数据,效率低

二、安全性高

  • 传统的进程:通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设
  • Binder 机制:从协议本身就支持对通信双方做身份校检,因而大大提升了安全性

Binder 通信模型

首先在理解模型之前先熟悉这几个概念:

  • Client 进程:跨进程通讯的客户端(运行在某个进程)
  • Server 进程:跨进程通讯的服务端(运行在某个进程)
  • Binder 驱动:跨进程通讯的介质
  • ServiceManager:跨进程通讯中提供服务的注册和查询(运行在 System 进程)

这里写图片描述

这里只是个简单的模型而已,只需理解模型的通讯流程:

  1. Server 端通过 Binder 驱动在 ServiceManager 中注册
  2. Client 端通过 Binder 驱动获取 ServiceManager 中注册的 Server 端
  3. Client 端通过 Binder 驱动和 Server 端进行通讯

Binder 通信原理

这里写图片描述

理解完模型流程之后,开始理解模型的通讯原理:

  1. Service 端通过 Binder 驱动在 ServiceManager 的查找表中注册 Object 对象的 add 方法
  2. Client 端通过 Binder 驱动在 ServiceManager 的查找表中找到 Object 对象的 add 方法,并返回 proxy 对象的 add 方法,add 方法是个空实现,proxy 对象也不是真正的 Object 对象,是通过 Binder 驱动封装好的代理类的 add 方法
  3. 当 Client 端调用 add 方法时,Client 端会调用 proxy 对象的 add 方法,通过 Binder 驱动去请求 ServiceManager 来找到 Service 端真正对象,然后调用 Service 端的 add 方法

Binder 对象和 Binder 驱动

  • Binder 对象:Binder 机制中进行进程间通讯的对象,对于 Service 端为 Binder 本地对象,对于 Client 端为 Binder 代理对象
  • Binder 驱动:Binder 机制中进行进程间通讯的介质,Binder 驱动会对具有跨进程传递能力的对象做特殊处理,自动完成代理对象和本地对象的转换

由于 Binder 驱动会对具有跨进程传递能力的对象做特殊处理,自动完成代理对象和本地对象的转换,因此在驱动中保存了每一个跨越进程的 Binder 对象的相关信息,Binder 本地对象(或 Binder 实体)保存在 binder_node 的数据结构,Binder 代理对象(或 Binder 引用 / 句柄)保存在 binder_ref 的数据结构

Java 层的 Binder

  • Binder 类:是 Binder 本地对象
  • BinderProxy 类:是 Binder 类的内部类,它代表远程进程的 Binder 对象的本地代理
  • Parcel 类:是一个容器,它主要用于存储序列化数据,然后可以通过 Binder 在进程间传递这些数据
  • IBinder 接口:代表一种跨进程传输的能力,实现这个接口,就能将这个对象进行跨进程传递
  • IInterface 接口:client 端与 server 端的调用契约,实现这个接口,就代表远程 server 对象具有什么能力,因为 IInterface 接口的 asBinder 方法的实现可以将 Binder 本地对象或代理对象进行返回

Binder 类和 BinderProxy 类都继承自 IBinder,因而都具有跨进程传输的能力,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。IBinder 是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分,但它不仅用于远程调用,也用于进程内调用。IBinder 接口定义了与远程对象交互的协议,建议不要直接实现这个接口,而应该从 Binder 派生。Binder 实现了 IBinder 接口,但是一般不需要直接实现此类,而是跟据你的需要由开发包中的工具生成,这个工具叫 aidi。你通过 aidi 语言定义远程对象的方法,然后用 aidi 工具生成 Binder 的派生类,然后使用它

AIDL

由于编译工具会给我们生成一个 Stub 的静态内部类,这个类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口,表明它具有远程 Server 承诺给 Client 的能力

一、服务端

在服务端中,我们只要实现 Stub 抽象类,和实现其方法即可

private IBinder myS = new IMyAidlInterface.Stub() {  
    @Override  
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {  

    }  

    @Override  
    public int add(int num1, int num2) throws RemoteException {  
        Log.i("Hensen", "从客户端发来的AIDL请求:num1->" + num1 + "::num2->" + num2);  
        return num1 + num2;  
    }  
}; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

二、客户端

在客户端中,可以通过 bindService 的回调中获取 AIDL 接口

private ServiceConnection conn = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);  
    }  

    @Override  
    public void onServiceDisconnected(ComponentName name) {  
        iMyAidlInterface = null;  
    }  
};

public void add(View view) {  
    try {  
        int res = iMyAidlInterface.add(1, 2);  
        Log.i("Hensen", "从服务端调用成功的结果:" + res);  
    } catch (RemoteException e) {  
        e.printStackTrace();
    }
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

梳理客户端的调用流程:

  1. 调用 Stub.asInterface 获取 BinderProxy 对象
  2. 调用 BinderProxy 对象的 add 方法

三、分析原理

1、Stub

Stub 类继承自 Binder,意味着这个 Stub 其实自己是一个 Binder 本地对象,然后实现了 IMyAidlInterface 接口,IMyAidlInterface 本身是一个 IInterface,因此他携带某种客户端需要的能力(这里是方法 add)。此类有一个内部类 Proxy,也就是 Binder 代理对象

/* 
 * This file is auto-generated.  DO NOT MODIFY. 
 * Original file: D:\\workspace5\\Boke\\app\\src\\main\\aidl\\com\\handsome\\boke\\IMyAidlInterface.aidl 
 */  
package com.handsome.boke;  
// Declare any non-default types here with import statements  

public interface IMyAidlInterface extends android.os.IInterface {  
    /** 
     * Local-side IPC implementation stub class. 
     */  
    public static abstract class Stub extends android.os.Binder implements com.handsome.boke.IMyAidlInterface {  
        private static final java.lang.String DESCRIPTOR = "com.handsome.boke.IMyAidlInterface";  

        /** 
         * Construct the stub at attach it to the interface. 
         */  
        public Stub() {  
            this.attachInterface(this, DESCRIPTOR);  
        }  

        /** 
         * Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface, 
         * generating a proxy if needed. 
         */  
        public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {  
            if ((obj == null)) {  
                return null;  
            }  
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
            if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {  
                return ((com.handsome.boke.IMyAidlInterface) iin);  
            }  
            return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);  
        }  

        @Override  
        public android.os.IBinder asBinder() {  
            return this;  
        }  

        @Override  
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {  
            switch (code) {  
                case INTERFACE_TRANSACTION: {  
                    reply.writeString(DESCRIPTOR);  
                    return true;  
                }  
                case TRANSACTION_basicTypes: {  
                    data.enforceInterface(DESCRIPTOR);  
                    int _arg0;  
                    _arg0 = data.readInt();  
                    long _arg1;  
                    _arg1 = data.readLong();  
                    boolean _arg2;  
                    _arg2 = (0 != data.readInt());  
                    float _arg3;  
                    _arg3 = data.readFloat();  
                    double _arg4;  
                    _arg4 = data.readDouble();  
                    java.lang.String _arg5;  
                    _arg5 = data.readString();  
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);  
                    reply.writeNoException();  
                    return true;  
                }  
                case TRANSACTION_add: {  
                    data.enforceInterface(DESCRIPTOR);  
                    int _arg0;  
                    _arg0 = data.readInt();  
                    int _arg1;  
                    _arg1 = data.readInt();  
                    int _result = this.add(_arg0, _arg1);  
                    reply.writeNoException();  
                    reply.writeInt(_result);  
                    return true;  
                }  
            }  
            return super.onTransact(code, data, reply, flags);  
        }  

        private static class Proxy implements com.handsome.boke.IMyAidlInterface {  
            private android.os.IBinder mRemote;  

            Proxy(android.os.IBinder remote) {  
                mRemote = remote;  
            }  

            @Override  
            public android.os.IBinder asBinder() {  
                return mRemote;  
            }  

            public java.lang.String getInterfaceDescriptor() {  
                return DESCRIPTOR;  
            }  

            /** 
             * Demonstrates some basic types that you can use as parameters 
             * and return values in AIDL. 
             */  
            @Override  
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {  
                android.os.Parcel _data = android.os.Parcel.obtain();  
                android.os.Parcel _reply = android.os.Parcel.obtain();  
                try {  
                    _data.writeInterfaceToken(DESCRIPTOR);  
                    _data.writeInt(anInt);  
                    _data.writeLong(aLong);  
                    _data.writeInt(((aBoolean) ? (1) : (0)));  
                    _data.writeFloat(aFloat);  
                    _data.writeDouble(aDouble);  
                    _data.writeString(aString);  
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);  
                    _reply.readException();  
                } finally {  
                    _reply.recycle();  
                    _data.recycle();  
                }  
            }  

            @Override  
            public int add(int num1, int num2) throws android.os.RemoteException {  
                android.os.Parcel _data = android.os.Parcel.obtain();  
                android.os.Parcel _reply = android.os.Parcel.obtain();  
                int _result;  
                try {  
                    _data.writeInterfaceToken(DESCRIPTOR);  
                    _data.writeInt(num1);  
                    _data.writeInt(num2);  
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  
                    _reply.readException();  
                    _result = _reply.readInt();  
                } finally {  
                    _reply.recycle();  
                    _data.recycle();  
                }  
                return _result;  
            }  
        }  

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);  
    }  

    /** 
     * Demonstrates some basic types that you can use as parameters 
     * and return values in AIDL. 
     */  
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;  

    public int add(int num1, int num2) throws android.os.RemoteException;  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153

2、asInterface

当客户端 bindService 的 onServiceConnecttion 的回调里面,通过 asInterface 方法获取远程的 service 的。其函数的参数 IBinder 类型的 obj,这个对象是驱动给我们的,如果是 Binder 本地对象,那么它就是 Binder 类型,如果是 Binder 代理对象,那就是 BinderProxy 类型。asInterface 方法中会调用 queryLocalInterface,查找 Binder 本地对象,如果找到,说明 Client 和 Server 都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回,如果找不到,说明是远程对象那么就需要创建 Binder 代理对象,让这个 Binder 代理对象实现对于远程对象的访问

/** 
 * Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface, 
 * generating a proxy if needed. 
 */  
public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {  
    if ((obj == null)) {  
        return null;  
    }  
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
    if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {  
        return ((com.handsome.boke.IMyAidlInterface) iin);  
    }  
    return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3、add

当客户端调用 add 方法时,首先用 Parcel 把数据序列化,然后调用 mRemote.transact 方法,mRemote 就是 new Stub.Proxy (obj) 传进来的,即 BinderProxy 对象

@Override  
public int add(int num1, int num2) throws android.os.RemoteException {  
    android.os.Parcel _data = android.os.Parcel.obtain();  
    android.os.Parcel _reply = android.os.Parcel.obtain();  
    int _result;  
    try {  
        _data.writeInterfaceToken(DESCRIPTOR);  
        _data.writeInt(num1);  
        _data.writeInt(num2);  
        mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  
        _reply.readException();  
        _result = _reply.readInt();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
    return _result;  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4、transact

BinderProxy 的 transact 方法是 native 方法,它的实现在 native 层,它会去借助 Binder 驱动完成数据的传输,当完成数据传输后,会唤醒 Server 端,调用了 Server 端本地对象的 onTransact 函数

public native boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
  • 1
  • 2

5、onTransact

在 Server 进程里面,onTransact 根据调用 code 会调用相关函数,接着将结果写入 reply 并通过 super.onTransact 返回给驱动,驱动唤醒挂起的 Client 进程里面的线程并将结果返回

@Override  
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {  
    switch (code) {  
        case INTERFACE_TRANSACTION: {  
            reply.writeString(DESCRIPTOR);  
            return true;  
        }  
        case TRANSACTION_add: {  
            data.enforceInterface(DESCRIPTOR);  
            int _arg0;  
            _arg0 = data.readInt();  
            int _arg1;  
            _arg1 = data.readInt();  
            int _result = this.add(_arg0, _arg1);  
            reply.writeNoException();  
            reply.writeInt(_result);  
            return true;  
        }  
    }  
    return super.onTransact(code, data, reply, flags);  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

AMS 的 Binder 体系

这里写图片描述

AMS 是 Android 中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,从图中可以看出:

AMS Binder 角色
IActivityManager IInterface
ActivityManagerNative Binder 本地对象
ActivityManagerProxy Binder 代理对象
ActivityManagerService Service 端
ActivityManager 普通管理类

结语

这里只是简单的理解下 Binder 机制的基本原理,后续有时间会研究 framework 层的知识,如果有兴趣的同学可以不依赖 AIDL 工具,手写远程 Service 完成跨进程通信,这样就可以加深对 AIDL 和 Binder 的理解,个人觉得这样是最好的记忆方式,一起来写吧

我们今天的关于Android源码的Binder权限是如何控制?全网独家首发!android binder原理的分享已经告一段落,感谢您的关注,如果您想了解更多关于2021届毕业生还没找到Android开发工作,全网独家首发!、Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?、Android service 起动顺序如何控制?、Android 进阶 ——Android 跨进程通讯机制之 Binder、IBinder、Parcel、AIDL的相关信息,请在本站查询。

本文标签: