binder学习笔记

进程间通信方式

  • 1、为什么android需要采用binder
bind1.png

IPC

bind4.png
  • 两次拷贝,先调用cope_form_user拷贝到缓存区,然后再拷贝到缓存区

Bind

bind5.png
  • 服务端和客户端都会映射到同一个空间
  • 通过mmap的内存映射,内核缓存区和服务端是两个都处于同一个空间
  • 共享内存是客户端,服务端和内核缓存区是处于一个空间,所以无需拷贝,问题是需要多次同步,所以存在问题
  • 很安全,APP是由系统分配UID的,并且支持实名

AIDL

自动生成的AIDL文件解析

客户端

public interface  ILeoAidl extends android.os.IInterface {
    /** 继承了binder 和aidl类  抽象类*/
    public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl{
    
        private static final java.lang.String DESCRIPTOR = "com.xx.leo_service.ILeoAidl";
            public Stub(){
        //
         this.attachInterface(this, DESCRIPTOR);
    }
    

    public static com.xx.leo_service.ILeoAidl asInterface(android.os.IBinder obj){
        //判断是否为空
        if ((obj==null)) {
          return null;
        }
        //查询本地的接口是否相等
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        //如果在同一个进程,就直接返回,否则就代理
        if (((iin!=null)&&(iin instanceof com.xx.leo_service.ILeoAidl))) {
        //直接返回
              return ((com.xx.leo_service.ILeoAidl)iin);
        }
        //返回代理
        return new com.xx.leo_service.ILeoAidl.Stub.Proxy(obj);
    }
    
    @Override 
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException   {
      switch (code)   {
    case TRANSACTION_addPerson:  {
        data.enforceInterface(DESCRIPTOR);
        com.xx.leo_service.Person _arg0;
        if ((0!=data.readInt())) {
        _arg0 = com.xx.leo_service.Person.CREATOR.createFromParcel(data);
    }  else {
        _arg0 = null;
    }
        this.addPerson(_arg0);
        reply.writeNoException();
        return true;
    }
    
    case TRANSACTION_getPersonList: {
        data.enforceInterface(DESCRIPTOR);
        java.util.List<com.xx.leo_service.Person> _result = this.getPersonList();
        reply.writeNoException();
        reply.writeTypedList(_result);
        return true;
    }
    }
         return super.onTransact(code, data, reply, flags);
    }
    
    //实体类 直接实现AIDL
    private static class Proxy implements com.xx.leo_service.ILeoAidl{
    private android.os.IBinder mRemote;
    Proxy(android.os.IBinder remote)   {
    mRemote = remote;
    }
    
    
    //直接实现自定义方法
    @Override 
    public void addPerson(com.xx.leo_service.Person person) throws android.os.RemoteException   {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        //校验,每个服务都有唯一的DESCRIPTOR
        _data.writeInterfaceToken(DESCRIPTOR);
        
        if ((person!=null)) {
            //传入数据  
            _data.writeInt(1);
            person.writeToParcel(_data, 0); 
          }else {
    _data.writeInt(0);
    }
        mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
        _reply.readException();
    }
    finally {
        _reply.recycle();
        _data.recycle();
    }
    }
        @Override
        public java.util.List<com.xx.leo_service.Person> getPersonList() throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.util.List<com.xx.leo_service.Person> _result;
            try {
            //校验,每个服务都有唯一的DESCRIPTOR
                _data.writeInterfaceToken(DESCRIPTOR);
                //调用服务端的代码
                mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
                //读取看是否有
                _reply.readException();
                _result = _reply.createTypedArrayList(com.xx.leo_service.Person.CREATOR);
            }
            finally {
                _reply.recycle();
                _data.recycle();
            } 
            return _result;
            }
    }
    
    //传入一个整型,按顺序拿到方法
        static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        

        static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    public void addPerson(com.xx.leo_service.Person person) throws android.os.RemoteException;
    
    public java.util.List<com.xx.leo_service.Person> getPersonList() throws android.os.RemoteException;
}

服务端

public interface ILeoAidl extends android.os.IInterface {
    public void addPerson(com.xx.leo_service.Person person)
        throws android.os.RemoteException;

    public java.util.List<com.xx.leo_service.Person> getPersonList()
        throws android.os.RemoteException;

    /** L核心方法  stub*/
    public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl {
        private static final java.lang.String DESCRIPTOR = "com.xx.leo_service.ILeoAidl";
        static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION +
            0);
        static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION +
            1);

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


      public static com.xx.leo_service.ILeoAidl asInterface(
            android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }

            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

            if (((iin != null) && (iin instanceof com.xx.leo_service.ILeoAidl))) {
                return ((com.xx.leo_service.ILeoAidl) iin);
            }

            return new com.xx.leo_service.ILeoAidl.Stub.Proxy(obj);
        }

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

        //对应客户端的onTransact方法
        @Override
        public boolean onTransact(int code, android.os.Parcel data,
            android.os.Parcel reply, int flags)
            throws android.os.RemoteException {
           //根据整型来拿到方法
            switch (code) {
case TRANSACTION_addPerson: {
                data.enforceInterface(DESCRIPTOR);

                com.xx.leo_service.Person _arg0;

                if ((0 != data.readInt())) {
                    _arg0 = com.xx.leo_service.Person.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }

                this.addPerson(_arg0);
                reply.writeNoException();

                return true;
            }

            case TRANSACTION_getPersonList: {
                data.enforceInterface(DESCRIPTOR);

                java.util.List<com.xx.leo_service.Person> _result = this.getPersonList();
                reply.writeNoException();
                reply.writeTypedList(_result);

                return true;
            }
            }

            return super.onTransact(code, data, reply, flags);
        }

 }
}

bind7.png

ServiceManager

我们自动生成的aidl和系统的服务是一一对应的。

bind6.png

ServiceManage负责管理所有的ServiceManager服务,在APP启动的时候,会调用电量,媒体等多个服务,都是通过binder来进行调用的,所有的服务端和客户端通信都是通过ServiceManager来注册和获取服务的。

bind2.png

A进程访问B进程时的几种状态

  1. 进程B,整个进程都没有启动
  2. 进程B启动了,但是里面的Service没创建出来
  3. 进程B启动了,里面的Service也创建了,但是Service没有被绑定过,回调onBind()
  4. 进程B启动了,里面的Service也创建了,但是Service已经被绑定过,回调onRebind()

5个角度来分析Binder

  1. 从性能的角度 数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存
  2. 从稳定性的角度
    • Binder是基于C/S架构的 C/S 相对独立,稳定性较好
    • 共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题
  3. 从安全的角度
    • 传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份
      1. 传统IPC只能由用户在数据包里填入UID/PID
      2. 可靠的身份标记只有由IPC机制本身在内核中添加
      3. 传统IPC访问接入点是开放的,无法建立私有通道
    • Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行
    • Android的UID权鉴是如何做的?
  4. 从语言层面的角度
    • Linux是基于C语言(面向过程的语言),而Android是基于Java语言(面向对象的语句)
    • Binder恰恰也符合面向对象的思想 Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中
    • Android OS中的Zygote进程的IPC采用的是Socket(套接字)机制,Android中的Kill Process采用的signal(信号)机制等等。而Binder更多则用在system_server进程与上层App层的IPC交互。
  5. 从公司战略的角度
    总所周知,Linux内核是开源的系统,所开放源代码许可协议GPL保护,该协议具有“病毒式感染”的能力,怎么理解这句话呢?受GPL保护的Linux Kernel是运行在内核空间,对于上层的任何类库、服务、应用等运行在用户空间,一旦进行SysCall(系统调用),调用到底层Kernel,那么也必须遵循GPL协议。 而Android 之父 Andy Rubin对于GPL显然是不能接受的,为此,Google巧妙地将GPL协议控制在内核空间,将用户空间的协议采用Apache-2.0协议(允许基于Android的开发商不向社区反馈源码),同时在GPL协议与Apache-2.0之间的Lib库中采用BSD证授权方法,有效隔断了GPL的传染性,仍有较大争议,但至少目前缓解Android,让GPL止步于内核空间,这是Google在GPL Linux下 开源与商业化共存的一个成功典范。

mmap:

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

  • 1、在物理内存开辟物理区域(用于存储数据)
  • 2、将这个物理区域与磁盘进行映射
  • 3、将物理地址转化为虚拟地址,返回给应用层

总结

bind8.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容