AIDL是Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。android提供了很多进程间通信的组件,像Activity、BroadcastReceiver和ContentProvider都可以实现进程间的通信。

为什么还要用AIDL这个东西呢?

有开发过蓝牙或者WIFI应用的朋友肯定都知道,要去操作它必须先获得一个管理类,比如WIFI的管理类是WifiManager,通过getSystemService(Context.WIFI_SERVICE)就可以得到wifi的管理权限,这个服务「Context.WIFI_SERVICE」提供了很多的方法可以让用户去操作WIFI,比如打开wifi可以调用setWifiEnabled(true)方法。

WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);  WifiManager wifiManager = (WifiManager)getSystemService("wifi");

那这个Manager到底做了什么工作呢?

其实这个Manager只是一个管理类,真正干活的另有其人,是一个叫WifiService的系统服务。在Android系统中有很多的Manager,wifi的管理类叫WifiManager,蓝牙的管理类叫BluetoothManager,但是,只要有xxxManager.java,就会有Ixxx.aidl,并且有xxxService.java。

AIDL的作用是什么?

aidl类是实现Manager和Service通信的桥梁。

我们知道了AIDL的作用,如果上面的讲解还不能让你知道AIDL是做什么的,我建议你百度会快一些,可以认为是中间接口,但是中间接口不只是单纯的作为传话筒的作用,比如HAL,你认为他只是传话筒吗?并不是,知道有层次概念后,对你的学习会帮助巨大,当然了,如果你是一个架构师,知道层次的概念后,对你的开发帮助更大。

标题说的oneway是代码里的一个变量判断,通过这个变量判断来决定binder的调用,我转载的这篇文章作者对binder不仅仅是喜爱,更是有深入的研究。

以下是正文,文章通过例子和代码告诉你binder系统是如何判断的,各位兄台,请笑纳~


1 前言

用AIDL的人应该都知道下面代码中start和stop方法定义成oneway代表这个Binder接口是异步调用。

interface IPlayer {oneway void start();//异步,假设执行2秒oneway void stop();//异步,假设执行2秒int getVolume();// 同步,假设执行1秒
}

1.1 什么是异步调用?

举个例子:假如Client端调用IPlayer.start(),而且Server端的start需要执行2秒,由于定义的接口是异步的,Client端可以快速的执行IPlayer.start(),不会被Server端block住2秒。

1.2 什么是同步调用?

举个例子:假如Client端调用IPlayer. getVolume(),而且Server端的getVolume需要执行1秒,由于定义的接口是同步的,Client端在执行IPlayer. getVolume()的时候,会被Server端block住1秒。

1.3 为什么会有同步调用和异步调用?

细心的读者已经发现了,其实一般使用异步调用的时候,Client并不需要得到Server端的执行Binder服务的状态或者返回值,这时候使用异步调用,可以有效的提高Client执行的效率。

2 提问

好像很多人都明白前面讲的意思,我就出几个问题考考大家

假设进程A中有如下两个Binder服务IPlayer1和IPlayer2,这两个服务都有两个异步的接口start和stop。

interface IPlayer1 {oneway void start();//异步,执行2秒oneway void stop();//异步,执行2秒
}
interface IPlayer2 {oneway void start();//异步,执行2秒oneway void stop();//异步,执行2秒
}

2.1 问题1

如果进程B和进程C同一时刻分别调用IPlayer1.start()和IPlayer2.start(),请问进程A能否同时响应这两次Binder调用并执行?

正确答案:可以同时执行。

2.2 问题2

如果进程B和进程C同一时刻分别调用IPlayer1.start()和IPlayer1.start(),请问进程A能否同时响应这两次Binder调用并执行?

正确答案:不能同时执行,需要一个一个排队执行。

2.3 问题3

如果进程B和进程C同一时刻分别调用IPlayer1.start()和IPlayer1.end(),请问进程A能否同时响应这两次Binder调用并执行?

正确答案:不能同时执行,需要一个一个排队执行。

如果回答正确并且知道原因的朋友,这个文章就可以不看了。如果回答错误或者蒙对了不清楚原因的朋友,请继续阅读文章帮你理解这些问题。

3 代码分析

话不多说,先看源码,我们首先来看看oneway的Binder调用在Binder Driver中的逻辑

/*** binder_proc_transaction() - sends a transaction to a process and wakes it up* @t:      transaction to send* @proc:   process to send the transaction to* @thread: thread in @proc to send the transaction to (may be NULL)*/
static bool binder_proc_transaction(struct binder_transaction *t,struct binder_proc *proc,struct binder_thread *thread)
{//找到Server端的对应Binder服务在Binder驱动中对应的对象binder_nodestruct binder_node *node = t->buffer->target_node;//判断这次Binder调用是不是onewaybool oneway = !!(t->flags & TF_ONE_WAY);//初始化为false,用于标记当前Server端的对应Binder服务是否正在执行oneway的方法bool pending_async = false;binder_node_lock(node);//oneway == trueif (oneway) {if (node->has_async_transaction) {//第2次oneway调用执行这里//发现对应Binder服务正在执行oneway的方法,设置pending_async为truepending_async = true;} else {//第1次oneway调用执行这里//发现对应Binder服务没有执行oneway的方法,设置has_async_transaction为1node->has_async_transaction = 1;}}binder_inner_proc_lock(proc);//如果发现Server端已经死亡,就直接返回了,正常不会执行if (proc->is_dead || (thread && thread->is_dead)) {binder_inner_proc_unlock(proc);binder_node_unlock(node);return false;}//oneway的调用thread为空,第1次oneway调用,pending_async为falseif (!thread && !pending_async)//第1次oneway调用会找到一个空闲的Server端线程,用于响应这次oneway调用thread = binder_select_thread_ilocked(proc);if (thread) {//第1次oneway调用,thread不为空,直接把这次Binder work放到thread的工作队列去执行binder_enqueue_thread_work_ilocked(thread, &t->work);} else if (!pending_async) {binder_enqueue_work_ilocked(&t->work, &proc->todo);} else {//第2次oneway调用,thread为空,pending_async为true,//这次Binder work放到Binder Node的async_todo队列中,不会立刻执行binder_enqueue_work_ilocked(&t->work, &node->async_todo);}if (!pending_async)//第1次oneway调用,thread不为空,所以需要唤醒thread执行工作队列中的Binder workbinder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);binder_inner_proc_unlock(proc);binder_node_unlock(node);return true;
}

对应到我们的三个问题,我们首先有这样子的前提,进程A中有两个Binder Server端IPlayer1和IPlayer2,也就是在Binder驱动中有两个binder node的结构体,并且进程A的Binder线程池处于空闲的状态。还有一点要明确的是,就算进程B和进程C同时发起Binder调用,但是在Binder驱动中还是有先后顺序,因为有一把锁binder_inner_proc_lock(proc)。

问题1解析:

因为进程B和进程C分别调用两个Binder服务,也就是两个binder node,所以进程B和进程C都会走如下的代码,也就是说进程A会有两个线程分别处理进程B的IPlayer1.start()和进程C的IPlayer2.start(),所以答案是同时执行

static bool binder_proc_transaction(struct binder_transaction *t,struct binder_proc *proc,struct binder_thread *thread)
{struct binder_node *node = t->buffer->target_node;bool oneway = !!(t->flags & TF_ONE_WAY);bool pending_async = false;binder_node_lock(node);if (oneway) {//不管是是进程B还是进程C,因为不是同一个binder_node,所以都是走false的逻辑if (node->has_async_transaction) {//不执行} else {node->has_async_transaction = 1;}}binder_inner_proc_lock(proc);if (!thread && !pending_async)//oneway调用会找到一个空闲的Server端线程,用于响应这次oneway调用thread = binder_select_thread_ilocked(proc);if (thread) {//oneway调用,thread不为空,直接把这次Binder work放到thread的工作队列去执行binder_enqueue_thread_work_ilocked(thread, &t->work);} else if (!pending_async) {//不执行} else {//不执行}if (!pending_async)//oneway调用,thread不为空,所以需要唤醒thread执行工作队列中的Binder workbinder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);binder_inner_proc_unlock(proc);binder_node_unlock(node);return true;
}
问题2解析:

我们假设先处理进程B的IPlayer1.start()的调用,进程B会执行和问题1中描述的代码一样的操作,唤醒进程A中的一个线程,处理这次进程B的IPlayer1.start()调用。

但是进程C的IPlayer1.start()调用逻辑就不一样了,应该是下面这个逻辑,也就是说进程A不会立刻处理进程C的IPlayer1.start()的调用。所以答案就是不能同时执行,需要一个一个排队执行。

static bool binder_proc_transaction(struct binder_transaction *t,struct binder_proc *proc,struct binder_thread *thread)
{struct binder_node *node = t->buffer->target_node;bool oneway = !!(t->flags & TF_ONE_WAY);bool pending_async = false;binder_node_lock(node);//oneway == trueif (oneway) {if (node->has_async_transaction) {//因为是进程C和进程B是同一个binder_node,进程B已经将has_async_transaction设置truepending_async = true;} else {//不执行}}binder_inner_proc_lock(proc);if (thread) {//不执行} else if (!pending_async) {//不执行} else {//这次Binder work放到binder_node的async_todo队列中,不会立刻执行binder_enqueue_work_ilocked(&t->work, &node->async_todo);}binder_inner_proc_unlock(proc);binder_node_unlock(node);return true;
}

那什么时候处理进程C的IPlayer1.start(),看下面代码,简单说就是会在处理完进程B的IPlayer1.start()之后,在释放进程B调用IPlayer1.start()申请的buffer的时候,处理进程C的IPlayer1.start()。

        case BC_FREE_BUFFER: {//准确释放进程B申请的bufferif (buffer->async_transaction && buffer->target_node) {struct binder_node *buf_node;struct binder_work *w;//先拿到这块buffer处理的binder node,也就是IPlayer1对应的binder nodebuf_node = buffer->target_node;binder_node_inner_lock(buf_node);//检查一下buf_node是否有未处理的oneway的binder workw = binder_dequeue_work_head_ilocked(&buf_node->async_todo);if (!w) {//不执行buf_node->has_async_transaction = 0;} else {//如果有未处理完的oneway的binder work,就将binder node保存的async_todo全部添加到进程A的todo。binder_enqueue_work_ilocked(w, &proc->todo);//唤醒一个线程去处理todo中的binder work,也就是进程C的IPlayer1.start()binder_wakeup_proc_ilocked(proc);}binder_node_inner_unlock(buf_node);}//释放进程B申请的buffertrace_binder_transaction_buffer_release(buffer);binder_transaction_buffer_release(proc, buffer, NULL);binder_alloc_free_buf(&proc->alloc, buffer);break;}
问题3解析:

虽然进程B和进程C同一时刻分别调用IPlayer1.start()和IPlayer1.end()两个不同的方法,但是两个进程调用的Server端都是IPlayer1,也就是binder node是同一个,所以答案和问题2一样。

4 思考一个问题

假如一个进程B,在短时间内,例如一秒内,调用1000次进程A的IPlayer1.start()会发生什么。
第1次IPlayer1.start():唤醒进程A的一个线程处理IPlayer1.start(),两秒之后完成
第2-1000次IPlayer1.start():发现IPlayer1对应的binder node正在处理一个oneway的方法,会把所有2~1000次的调用放到binder node的async_todo队列中,等第一次IPlayer1.start()执行完成之后,释放buffer的时候,才能去统一处理这些async_todo中保存的第2-1000次。

那么问题就来了,虽然第2-1000次的调用不会立刻执行,但是已经在进程A中申请了所有的2~1000次IPlayer1.start()所需要的buffer,一个zygote进程A,最大oneway请求的buffer上限为(1MB -8KB)/2 = 508KB,不懂的可以看[007]一次Binder通信最大可以传输多大的数据?这个博客,假设一次IPlayer1.start(),需要申请1KB的buffer,也就意味这在第509次IPlayer1.start()的时候,无法申请到buffer从而导致IPlayer1.start()的Binder调用失败。

在[011]一个看似是系统问题的应用问题的解决过程中解决的就是这个问题。

5 小结

Binder机制是一个非常牛逼的机制,里面有很多小的细节值得我们去深挖,只有完全理解Binder驱动,才能从微观的角度去解决宏观的问题。

点击阅读原文,在PC端获得更好的阅读体验


http://www.taodudu.cc/news/show-4133939.html

相关文章:

  • 看你简历上写熟悉 AIDL,说一说 oneway 吧
  • Kafka之sync、async以及oneway
  • 关于AIDL接口定义中oneway的修饰符源码解析.
  • 单调栈典型题目
  • 单调栈及单调栈的应用
  • 详解单调栈算法
  • 单调栈和单调队列的本质区别
  • 单调队列java_单调队列单调栈
  • 单调栈算法详解
  • 单调栈简介
  • 单调栈详解
  • 单调队列和单调栈
  • 理解单调栈与单调队列
  • python项目-Python 的练手项目有哪些值得推荐?
  • python练手经典100例项目-Python 的练手项目有哪些值得推荐?
  • Java SE学习笔记
  • 004@ kernel 的配置和编译总结 分析2
  • 什么是运算计算机科学的基础概念,【软件设计师】笔记一:计算机科学基础知识...
  • 钉钉获取用户信息 php,钉钉开发c#帮助类 获取用户信息 DingHelper.cs
  • java五子棋技术路线,一位老码农的编程简史
  • Linux文件管理之(高级)、⽂件查找、上传与下载、输出与重定向、字符处理命令、打包压缩解压缩、文件系统
  • 软件设计师2022记录
  • 软设
  • 李建忠「设计模式」笔记
  • 郑州大学083500软件工程复试专业课复习
  • 再谈编程语言设计和实现
  • BT5的U盘完整安装
  • 编译原理LL(1)文法-判断,first,follow,select,分析字符串
  • UE4HTTP下载网速过慢问题
  • UE4 从文件夹读取音乐文件并播放

Android-你真的懂AIDL的oneway嘛?相关推荐

  1. 大话android 进程通信之AIDL

    上一篇的service涉及到进程通信问题,主要解决办法是通过 messenger来发送消息,这也是Google推荐的进程通信方式,比较简单易懂嘛~~,messenger底层也是通过binder来实现的 ...

  2. 面试官问你Java线程池--怎么样回答才能让面试官知道你真的懂了!

    一.引言 不管是Java面试还是Android面试,线程池都是面试官高频考察的点,那我们怎么回答,才能让面试官了解到我们是真的懂Java线程池了呢?这篇文章不涉及到线程池的使用和原理,如果你还不知道怎 ...

  3. 你真的懂了Camera的尺寸参数了吗?

    你真的懂了Camera的尺寸参数了吗? 本文针对自定义相机开发中,使用opengl渲染时,引发的最终视图变形问题 开发相机的过程中,你是否遇到过你想要的是右边图效果,结果出来的却是左边图效果,如果你遇 ...

  4. Android: 彻底搞懂Lifecycle——使用篇

    系列文章目录 第一章 Android: 彻底搞懂Lifecycle--使用篇 第二章 Android: 彻底搞懂Lifcycle--原理篇 文章目录 系列文章目录 前言 一.Lifecycle是什么? ...

  5. 不得不说的Android Binder机制与AIDL

    说起Android的进程间通信,想必大家都会不约而同的想起Android中的Binder机制.而提起Binder,想必也有不少同学会想起初学Android时被Binder和AIDL支配的恐惧感.但是作 ...

  6. Android应用中通过AIDL机制实现进程间的通讯实例

    Android中,每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢?显然,Java中是不支持跨进程内存共享的,因此要传递对象,需要把对象解析成操作系统能够理解的数据格式,以达 ...

  7. 示波器_你真的懂示波器嘛?面试中会用到的示波器知识

    示波器是电子工程师经常使用到的电子测量仪器,用途十分广泛,可将肉眼看不见的电信号变换成看得见的图像,便于人们研究各种电现象的变化过程.利用示波器能观察各种不同信号幅度随时间变化的波形曲线,还可以用它测 ...

  8. Android之四大组件(AIDL Service的使用)

    跨进程调用Service(AIDL Service) Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信. 在前一篇文章(关于Android中的四大组件(Se ...

  9. “三次握手,四次挥手”你真的懂吗?

    来源:码农桃花源 解读:"拼多多"被薅的问题出在哪儿?损失将如何买单? 之前有推过一篇不错的干货<TCP之三次握手四次挥手>,前几天有兄弟投稿,开始还以为是同一篇,后经 ...

  10. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截...

    程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少.数据 ...

最新文章

  1. Datawhale数据分析教程来了!
  2. Mapreduce 任务提交源码分析1
  3. Java 实现单例模式的 9 种方法
  4. centos7 rpm方式离线安装mysql注意点:需先卸载mariadb(rpm -e mariadb-libs --nodeps)
  5. LeetCode MySQL 1241. 每个帖子的评论数
  6. deepin配置JDK
  7. 分析称2015年手机文娱将激增至540亿美元
  8. 好奇怪呀后面加什么标点_好奇怪,为什么开发商就不能把这款好户型打造得更完美些呢?...
  9. 关于第十届省赛失败的总结
  10. Unity3D 手机竖屏设置(For Android)
  11. springboot微信点餐系统的设计与实现 毕业设计-附源码221541
  12. 服务器系统装显卡驱动,GPU服务器安装NVIDIA显卡驱动
  13. 【三星篇】三星手机实用功能软件推荐
  14. csr867x入门之串口AT指令协议(三)
  15. 一对一直播源码、一对一聊天app源码前端后台功能说明
  16. flume安装以及应用
  17. 华为技术有限公司总裁任正非
  18. layer.msg 延迟几秒跳转页面
  19. D17 STP生成数协议
  20. 数学建模——种群竞争模型

热门文章

  1. Milton 1.5.1发布,开源服务器端类库
  2. 学前端,学线下班还是丁鹿学堂或者慕课网?
  3. 查杀病毒实战----------------》ddg.223 and AnXQV
  4. 约翰霍普金斯大学计算机博士收入,约翰霍普金斯大学计算机科学研究生项目详情!...
  5. 箭头函数写法_JavaScript|箭头函数的用法
  6. 陈佩斯曾受邀喜剧综艺:被酬劳吓的恍惚好几天
  7. Apache Ranger安全区介绍
  8. 云上架构和传统IT架构的区别在哪里?(企业CIO、CTO必读)
  9. [Vue][transition]Vue中实现类似JQuery中slideUp slideDown的滑动显示隐藏过渡动画效果
  10. 031_组件 reRender控件和rendered控件的使用