android-----我眼中的Binder

        Binder作为进程间通信方式(IPC)的一种,算Android中比较难理解的部分了,今天计划以自己所认识的framework层的Binder原理来做个总结,好了,我们开始吧!

        Android中利用Binder通信,首先肯定需要获得Binder对象了,但是系统服务和我们自定义服务Binder对象的获取方式是不一样的,原因就在于系统服务是在系统启动的时候被注册到ServiceManegr的,我们只需要通过ServiceManager.getService(String name)传入想要获得的系统服务的名字就可以获得其对应的Binder对象进行进程间通信了,系统已经将该Service所提供的服务内容全部写好了;但是对于我们自定义的服务想要实现进程间通信的话,Client端和Server端都是我们自己写的,所以Android提供了AIDL来帮你简化这个过程,而Server端的实现一般是借助于Service来完成的,下面我们分别从系统服务和自定义服务两个方面来进行分析;

      先来看系统服务:

        首先从系统刚刚启动说起,init进程会启动一个叫ServiceManager的进程,该进程启动之后会做三件事:(1)通过open打开设备文件/dev/binder,将该文件中的内容通过mmap映射到本进程空间中;(2)通过IO控制命令BINDER_SET_CONTEXT_MGR将当前进程注册到Binder驱动中,Binder驱动便会为他在内核空间创建一个称为binder_context_mgr_node的节点出来,这个节点也就是ServiceManager在内核空间的Binder实体了,并且将他的句柄设置为0,他的创建是在系统启动的时候,这个节点在Binder驱动中是唯一的,所以也就造成了ServiceManager区别于其他Server了,但他仍然是运行在用户空间的;(3)ServiceManager启动之后就会无限循环等待Server和Client之间的进程间通信请求了;接着Zygote进程会孵化出一个子进程SystemService,我们的大部分系统服务比如ActivityManagerService、WindowManagerService、MediaPlayService等都是该进程内的一个线程,这些服务会通过调用ServiceManager.addService方法添加到ServiceManager中的服务列表svclist中,这样我们的系统服务就已经都注册到了ServiceManager里面了;

        上面讲了ServiceManager,他是随着系统的启动而创建的,它不同于普通的Server,甚至可以理解为是普通Server的Server,系统会将系统所要提供的服务注册到它的服务列表里面,服务列表中存储的便是服务名字+服务在Binder驱动中的句柄,这里的句柄可以理解为同一进程内部的引用,只不过现在是跨进程通信换了个名字而已,如果我们的应用想要用到某个系统服务的话,可以通过传入服务名字调用ServiceManager.getService得到该服务对应的Binder对象的方式进行获取,接着通过这个Binder对象进行相应操作即可,具体这就是ContentProvider为我们封装的内容了;

        Binder是通过客户端/服务端模式实现的,要想明白Binder的实现原理,我们需要明白几个概念之间的关系:Server/Client/ServiceManager/Binder驱动,他们四个是Binder架构的核心,尤其是ServiceManager和Binder驱动,因为我们的应用程序安装到系统上的时候,都会被分配一个唯一的UID用户ID,他是运行在独立进程中的,Linux基于用户安全的考虑是不允许我们直接访问系统上服务的,因为他们分属于不用的进程,进程间的数据是不能共享的,我们要想访问这些系统服务就涉及到了进程间通信了,那么势必就需要内核空间的参与了,所以用到了Binder驱动,它有点类似于桥的作用;那么平常我们是怎么做到访问系统服务的呢?比如媒体资源,是通过ContentProvider,他的底层实现就是Binder,只不过被封装了而已;

        我们可以通过一个例子来说明他们四者之间的关系,假如你想问你同学借点钱,那么你现在就是Client端,你同学就是Server端,但是你两是异地的,不能直接见面,怎么办呢,打电话呗,首先你先从通讯录里面找到你同学的电话,这里的通讯录就是ServiceManager了,然后打过去借就好了,那么电信运营商就是Binder驱动了,没有他你两根本联系不起来;

        讲完了所有涉及到的概念之后,就该看看是怎么通信的了,也就是ServiceManager.addService和ServiceManager.getService到底做了些什么事了?

        首先来说说ServiceManager.addService,也就是我们的Server是怎么和Binder驱动通信的:

        ServiceManager.addService(String name, IBinder service):传入的内容是Server的名字和该Server在Binder驱动中对应的对象;

        (1):Server首先将自己作为对象,并且附上一个句柄为0的值(用于访问ServiceManager),将这些内容封装成一个数据包,open有关Binder的设备文件/dev/binder,将封装好的该数据包发送给Binder驱动;

        (2):Binder驱动在收到这个数据包之后,发现里面存在一个Server对象,首先会在Binder驱动自己里面新建该Server对应的Binder实体,并赋予一个大于0的句柄,同时会将该句柄也加入到数据包中,接着将该数据包发送到句柄为0对应的对象上面,也就是ServiceManager上面了;

        (3):ServiceManager收到转发给自己的数据包之后,会查看其服务列表svlist中是否已经存在当前Server名字的服务,不存在的话,会将当前服务+当前服务对应于Binder驱动中的句柄加入到列表中;

        这样,系统服务就注册到ServiceManager中了;

        接下来便是ServiceManager.getService部分了,也就是Client是怎么和Binder驱动进行通信,返回对应请求的Binder对象,也就是该Server在Binder驱动中的句柄的:

        ServiceManager.getService(String name):传入的参数是将要请求的服务的名字

        (1):Client首先会将要获取的服务的名字以及一个句柄为0的值(为了访问ServiceManager)封装成一个数据包,open有关Binder的设备文件/dev/binder,将该数据包发送给Binder驱动;

        (2):Binder驱动在收到数据包之后发现里面有句柄为0的信息,就将该数据包转发给了ServiceManager来进行处理了;

        (3):ServiceManager在收到数据包之后根据服务的名字查看自己的服务列表svclist,找到之后会将其对应的在Binder驱动中的句柄信息也封装成一个数据包;

        (4):该数据包也会通过Binder驱动被发送给Client端;

        (5):Client端在收到数据包之后,就得到了自己所请求的服务在Binder驱动中的句柄,他会利用这个句柄信息在自己本地创建一个远程Server的代理,以后Client发消息都是发给这个代理的,随后的通信便变成了代理通过Binder驱动与真正的Server进行交互了,以此完成跨进程间的通信;

        这样系统服务是怎么注册到ServiceManager里面以及我们怎么获得这些服务对应于Binder驱动的句柄也就是Binder对象的过程讲解就已经结束了,接下来便是我们自定义服务是怎么通过Binder进行进程间通信的呢?

     自定义服务:

        这里就要用到Android为我们自定义进程间通信所提供的AIDL文件了,没有这个文件,你完全也可以实现跨进程通信,但是序列化,反序列化数据的封装都将需要你自己来实现,有了AIDL之后这个过程会显的比较简单,你并不需要关心序列化反序列化的顺序问题,只需要将Server进程想要提供给Client进程访问的方法定义在一个.aidl文件中即可,我们在此将他命名为Ixxx.aidl,那么系统将会为该AIDL文件自动生成对应的Ixxx.java文件;

        简单说说Ixxx.java的类结构,将是下面这样的伪代码形式:

public interface Ixxx extends IInterface
{
	public static abstarct class Stub extends Binder implements Ixxx
	{
		public static Ixxx asInterface(Binder binder){}
		public Binder asBinder(){}
		public boolean onTransact(int code,Parcel data,Parcel reply,int flags){}
		private static class Proxy implements Ixxx
		{
			public Binder asBinder(){}
			//Ixxx接口中的一些实现方法,当然这些实现并不是真正的逻辑实现,而只是通过transcat进行的一些进程切换操作
		}
	}
}

       可以看到Ixxx.java中存在一个Stub静态抽象类和Prxoy静态类,最关键的方法就是Stub类中的asInterface了,他会根据我们传入的Binder对象来判断是跨进程通信还是进程内部通信,如果是进程内部通信,该方法返回的将是Ixxx.Stub对象;如果是跨进程
通信返回的将是Ixxx.Stub.Proxy对象,这个代理对象中将的方法会通过调用transact方法来进行内核态的切换;

        下面我以文字的方式简述下我们自定义服务实现跨进程通信的原理:

        (1):首先,我们需要创建一个AIDL文件,将其命名为Ixxx.aidl,里面定义了服务端进程想要提供给客户端进程的方法列表,系统会为我们生成一个Ixxx.java文件;

        (2):我们的自定义服务端主要是通过service来实现的,在service里面实现具体提供给客户端的方法的操作代码,并且通过onBind方法返回此服务端对应的Binder对象,而后我们的客户端通过bindService绑定服务端的时候就可以获得这个服务端的Binder对象了;

        (3):在客户端获得Binder对象之后会调用Ixxx.Stub的asInterface方法将Binder对象传入,获得Ixxx对象,在这里就将跨进程和通进程通信分开了,如果是跨进程通信的话asInterface返回的是Ixxx.Stub.Proxy代理对象,那么以后客户端调用服务端的方法实际上是首先调用的Ixxx.Stub.Proxy代理对象里面对应于服务端的方法,这个代理对象的方法会通过transact陷入内核态来进行实际上的进程间通信调用服务端的onTransact方法,在onTransact方法中会根据标志调用不同的服务端方法;如果是同进程通信的话,asInterface返回的是Ixxx.Stub对象,则直接调用服务端方法,没有必要陷入内核态来执行了;

        这也便是我们自定义服务实现进程间通信的简单过程了;

       

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
使用xml作为数据交互的载体是Android中非常重要的功能,比如天气预报数据、短信备份数据、通讯录数据都可以以xml的格式通过网络传输。 为了演示Xml数据的操作,我模拟了一个短信备份的案例。 需求:界面如图1-10所示。上面是三个Button,前两个分别对应两种不同方式生成xml,第三个Button点击后解析备份的xml文件,然后将数据展现在下面的ScrollView中。短信数据是模拟的假数据。 生成的xml格式如文件1-10。 【文件1-10】 xml文件格式 1. ?xml version="1.
这篇博客我们来介绍一下责任链模式(Chain-of-responsibility Pattern),责任联模式又称为职责链模式,是 行为型设计模式 之一。顾名思义,责任链模式中存在一个链式结构,多个节点首尾相连,每个节点都可以被拆开再连接,因此,链式结构具有很好的灵活性。将这样一种结构应用于编程领域,将每一个节点看作是一个对象,每一个对象拥有不同的处理逻辑,将一个请求从链式的首段发出,沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止,这就是责任链或者职责链的通俗定义。 转载请注明出处: h

GeekBand第十一周笔记 - 2016-07-25 17:07:19

本周的主要内容介绍Gradle,NDK,管理依赖和Git等 一、Gradle Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。 面向Java应用为主。当前其支持的语言限于Java、Groovy和Scala,计划未来将支持更多的语言。 Coding只是软件开发中的一个部分 编译源代码 运行单元测试和集成测试 执行静态代码分析,生成分析报告 创建分布版本 部署到目标环
在安卓开发中,谷歌已经为我们提供了许多原生控件,基本上能够满足我们日常的开发需求,但是某些项目中原生控件可能达不到产品所要求的各式各样的酷炫效果或功能效果,这个时候我们只能自己自定义控件来满足项目需求,我们知道自定义控件包括三种方式: 1继承控件,即继承谷歌提供的原生控件,在此基础上提供一些原生控件不具备的功能,如github上各种酷炫效果的开源组件基本上都是采用的这种方式. 2组合控件:即组合多个原生控件来达到某些单个原生控件原本不具备的功能,这个在日常开发中应该是使用的比较多的,如基本上每个App都存

Android多点触摸交互处理 - 2016-07-25 17:07:15

安卓手机中,多点触摸是是最基本的操作,下面就使用程序进行演示多点触摸操作 一、获取触摸事件中的点击,移动和抬起事件,创建新的安卓项目,如下面代码所示,分别为MainActivity类代码和Layout布局文件,用于实现获取点击事件 1.1、MainActivity类中代码,代码中为布局对象root添加监听事件,使用switch进行对屏幕时间进行判断,用于判断事件的按下还是移动,在代码中      root=(RelativeLayout) findViewById(R.id.content);用于绑定布局
内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出。 前言:ListView——列表,它作为一个非常重要的显示方式,不管是在Web中还是移动平台中,都是一个非常好的、不开或缺的展示信息的工具。在Android中,ListView控件接管了这一重担,在大量的场合下,我们都需要使用这个控件。虽然在Android 5.X时代,RecyclerView在很多地方都在逐渐取代ListView,但ListView的使用范围依然非常的广泛,它这万年老大哥的地位也不是轻易就能撼动的。下面就介绍
Android ListView使用简介 ListView 是 Android 软件开发中十分常用也十分重要的一个 UI 控件。 ListView 的每一个子项可以是一个简单的字符串,也可以是一组 View 的组合,开发者完全可以根据自己的需求来定义显示的形式。 如何使用一个 ListView 实现对数据的显示呢 ? 1. 创建 ListView 控件,已备数据显示 2. 准备要显示的数据 3. 为 ListView 构建一个数据适配器 (Adapter) 4. 绑定适配器 5. 处理 ListView
转载请标明出处: 一片枫叶的专栏 文本我们将讲解android studio打包apk,aar,jar包的相关知识。apk包就是android系统的安装包,这里没什么好说的,aar包是android中独有的类库包,而jar包是java中特有的类库包,在具体的介绍打包之前,我们先来aar包和jar包的区别。 jar包与aar包的区别 jar是java字节码文件(class文件)的归档文件,其不包含android中的资源文件等信息; aar是android中特有的归档文件,既包含字节码文件也包含android
欢迎转载,转载请注明出处: http://blog.csdn.net/dmk877/article/details/51980734 在上一篇文章中,我们详细讨论了Tween动画的xml的实现以及interpolator的使用,相信通过上篇文章大家对Tween动画的xml属性的配置会有一个详细的理解,当然这篇文章也是承接上篇文章,所以强烈建议先阅读上篇文章: Android开发之Tween(补间动画)完全解析(上) ,这篇文章将从代码的角度实现上篇文章的效果。 如有疑问请留言,如有谬误欢迎批评指正。 Tw

Android闹钟设置的解决方案 - 2016-07-25 14:07:00

Android设置闹钟并不像IOS那样这么简单,做过Android设置闹钟的开发者都知道里面的坑有多深。下面记录一下,我解决Android闹钟设置的解决方案。 主要问题 API19开始AlarmManager的机制修改。 应用程序被Kill掉后,设置的闹钟不响。 6.0以上进入Doze模式会使JobScheduler停止工作。 手机设置重启后,闹钟失效问题。 API19以上AlarmManager机制的修改 API19之前AlarmManager提供了三个设置闹钟的方法,由于业务需求闹钟只需要一次性,所以