理解Binder框架

Binder是Android系统进程间通信(IPC)最重要的方式。要想了解Android的系统原理,必须要先对Binder框架有一定的理解。Binder是什么?Binder可以理解为能在进程间进行"通信"的对象,这个通信不是指在不同进程中操作同一个对象,而应理解为一种通信协议。

一、Binder的引入背景

传统的进程间通信方式有管道,消息队列,共享内存等,其中管道,消息队列采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。Binder通过内存映射的方式,使数据只需要在内存进行一次读写过程。

内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,相反,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间<---->用户空间两者之间需要大量数据传输等操作的话效率是非常高的。

二、Binder的通信模型

主要分为4个部分Binder驱动,Client,Server,ServiceManager(SM),其中Client,Server,ServiceManager(SM)指的是用户空间,并且分别在不同的进程。Binder驱动是内核空间,Binder驱动是一段c语言实现的代码。

服务分系统服务和本地服务,系统服务一般指设备开机时就创建供所有应用使用的服务,如AMS,PMS等,本地服务一般指本地创建的Service,一般供当前应用使用。

通信模型.png

由上图可以看出Binder通信的流程是这样的:

  • 首先不同的服务Server一般为service需要先在SM上进行注册
  • Client想获取服务时,先到SM上通过服务名请求服务
  • 得到对应的服务的引用,通过这个引用进行进一步的通信。

由于Client、Server、SM不在同一个进程,所以都需要借助Binder驱动完成通信。这其中有几个关键点。先明确几个概念,Binder Server对象是指真正的进行服务的对象,Biner内核对象是根据Binder Server对象在内核空间的一种表述方式,因为语言层面不同,所以具体的表现形式也不同。比如用应用层表现为Java类或C++类,在内核层表现为结构体了。也可以称为实体,主要是和引用区分开,实体只有一个,而引用可以有很多。

  1. Client、Server、SM不在同一个进程,最初Client和Server怎么得到SM的通信的?
  2. 具体的注册流程是怎样的?
  3. Binder内核对象,BinderProxy对象是同一个对象吗,有什么联系?

首先第一个问题,首先ServiceManage进程本身就是也是采用Binder通信,在系统初始化的时候使用BINDER_SET_CONTEXT_MGR命令将自己注册为ServiceManage,这个过程会在内核空间产生一个ServiceManage的Binder实体。而这个Binder就在内核的0号引用,其他进程通过0号引用找到ServiceManage的Binder实体,通过内存映射就可以和SM建立联系。

第二个问题,首先拥有服务名的Server进程向SM注册时需要借助Binder驱动,此时创建了一个在内核的实体对象,和Server的对象是不同的结构,但拥有Server实体对象的关键信息,比如可以提供的方法等。这个实体对象有一项数据保存Server实体的引用(在内存中可以理解为映射的地址),借助Binder驱动将内核中的实体对像的引用和服务名注册到SM中,这样就有这样一条关系链,SM中服务名—>内核的实体对象的引用—>Server实体对象。

第三个问题,Binder内核对象,BinderProxy对象是同一个对象可定不是同一个对象,因为对应不同的空间,语言层面都有不一样,BinderProxy是系统根据Binder内核对象在Cilent进程新建(new)的一个Binder对象,里面包含Binder内核对象的关键信息,比如所能提供的方法等,这个对象就和真正远程的Server实体对象很相似了,这样Client对象就能像操作Server实体对象一样进行操作,不用考虑Server实在远程还是本地。但具体方法的实现还要通过代理的方式调用远程对象来实现。一般通过transact()和onTransact()来实现代理的调用。

常见的Binder通信过程分析

1. 利用Binder不使用AIDL实现IPC的过程
服务端:
public class NoAidlService extends Service {    
    public static final int TRANSACTION_studyBinder = 0x001;    
    private static final String DESCRIPTOR = "NoAidlService";    
    private Binder mNoAidlBinder = new NoAidlBinder();    
    @Nullable    
    @Override    
    public IBinder onBind(Intent intent) {      
        return mNoAidlBinder;    
    }    
    private class NoAidlBinder extends Binder {        
        @Override        
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 
        throws RemoteException  {            
            switch (code) {               
                case TRANSACTION_studyBinder: {                    
                    data.enforceInterface(DESCRIPTOR);                    
                    String _arg0;                    
                    _arg0 = data.readString();                    
                    String _result = _arg0+" Study NoAidlService";                    
                    reply.writeNoException();                    
                    reply.writeString(_result);                    
                    return true;                
                }            
            }            
            return super.onTransact(code, data, reply, flags);       
       }   
   }
}

注册服务,并指定为远程进程
<service  
android:name=".NoAidlService"    
android:process=":remote" />   

客户端:
private ServiceConnection mNoAidlConnection = new ServiceConnection() {        
    @Override        
    public void onServiceConnected(ComponentName name, IBinder clientBinder) {           
       android.os.Parcel _data = android.os.Parcel.obtain();            
       android.os.Parcel _reply = android.os.Parcel.obtain();            
       String _result;           
       try  {                
           _data.writeInterfaceToken("NoAidlService");                
           _data.writeString("SilenceDut");                
           service.transact(NoAidlService.TRANSACTION_studyBinder, _data, _reply, 0);               
           _reply.readException();                
           _result = _reply.readString();     
           mResultTv.setText(mResultTv.getText()+"\n"+_result);                           
        } catch (RemoteException e)  {                
           e.printStackTrace();            
        } finally {                
          _reply.recycle();                
         _data.recycle();            
        }        
    }        
    @Override       
    public void onServiceDisconnected(ComponentName name) {       
    }    
};         

绑定服务:
Intent intent = new Intent(this,NoAidlService.class);         
bindService(intent, mNoAidlConnection, Context.BIND_AUTO_CREATE);

具体的过程是NoAidlService需要先在Manifest注册信息,这相当于是将服务信息注册到ServiceManage。客户端用Intent将NoAidlService的服务名等信息包装,通过bindService在ServiceManage里寻找NoAidlService,如果成功,则通过onServiceConnected回调得到服务端的信息ComponentName,和服务通信接口clientBinder。

这个clientBinder和NoAidlService通过onBind返回的mNoAidlBinder相似,如果是远程服务,就不是同一个对象,可以理解为是通过Binder驱动得到的一个代理ProxyBinder对象,但看起来和mNoAidlBinder是同一个对象,其实不是的。但如果在一个进程,两个就是同一个对象。

clientBinder通过transact指定服务端对应的函数code比如TRANSACTION_studyBinder,来调用服务端的相应函数,通过Binder驱动,最后会调用到服务端NoAidlBinder的onTransact,通过判断code来确定相应的函数,然后通过再将结果返回,这个过程对Client是阻塞性的,所以客户端调用最好是在异步线程和服务端通信。

对服务端,系统会为每个服务提供线程池,这也容易想到,因为不可能让不同的服务为在调用服务时相互等待。

2. 使用AIDL来和Service通信AIDL

AIDL实现IPC通信Demo
AIDL即Android Interface Definition Language,Android接口定义语言。它是一种IDL语言,可以拿来生成用于IPC的代码。

其实就是AIDL文件生成一个帮助类,屏蔽parcel的读写细节,让客户端使用者专注于业务的实现。有一点需要注意的是,如果Service不是在一个新的进程,通过IBinder.queryLocalInterface(DESCRIPTOR)的方式得到本地的Binder对象,也就是和onBind返回的对讲是同一个,就不会涉及到跨进程通信和Binder驱动的调用。这点和不使用AIDL实现IPC是一样的。

参考:【 Android Bander设计与实现 - 设计篇

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

推荐阅读更多精彩内容