超简单的Binder,AIDL和Messenger的原理及使用流程

Binder的原理

要想了解AIDL就需要先了解Binder的原理,所以这里先说一下Binder原理,Binder的原理大概是这样:

服务器端:当我们在服务端创建好了一个Binder对象后,内部就会开启一个线程用于接收binder驱动发送的消息,收到消息后会执行相关的服务器代码。

Binder驱动:当服务端成功创建一个Binder对象后,Binder驱动也会创建一个mRemote对象,该对象的类型也是Binder类,客户就可以借助这个mRemote对象来访问远程服务,注意这里是借助,真正调用的时候需要将这个转换成对应的对象,比如使用AIDL的时候就要转换成AIDL对象。

客户端:客户端要想访问Binder的远程服务,就必须获取远程服务的Binder对象在binder驱动层对应的mRemote引用。当获取到mRemote对象的引用后,就可以调用相应Binder对象的暴露给客户端的方法(如果有方法的话)。

AIDL

AIDL的本质其实就是系统为我们提供了一种快速实现Binder的工具,我们完全可以不用AIDL,自己去写代码实现Binder,但是当你写出来的时候会发现其实和AIDL自动生成的代码一模一样。我们接下来来分析一下原理,因为AIDL的实现其实就是快速实现Binder,所以原理自然离不开Binder。但是在分析原理之前,我们先将系统根据我们定义的AIDL文件自动生成的java文件分析一下。比较重要的就是Stub和它的内部代理类Proxy。我们说一下重要的方法:

asInterface(android.os.IBinder obj)
用于将服务器的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于统一进程,那么返回服务器的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。

onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)
这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由服务端onTransact方法来处理。这个方法有四个参数,分别是codedatareplyflags.code是确定客户端请求的方法是哪个,data是目标方法所需的参数,reply是服务器端执行完后的返回值。如果这个方法返回false,那么客户端的请求会失败。

Proxy#getBookList
这里的getBookList方法就是在自定义的AIDL文件中定义的方法,这个方法运行在客户端,当客户端远程调用此方法的时候,内部实现是这样的:首先在代理类中创建该方法所需要的输入型Parcel对象_data,输出型Parcel对象_reply和返回值对象List;然后把该方法的参数信息写入_data中,接着mRemote调用transact方法来发起RPC(远程过程调用)请求, 同时当前线程挂起,然后服务端的onTransact方法会被调用,直到RPC返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果并返回(如果有返回值的话),之前创建的参数其实就是onTransact()方法需要的参数。

说完了重要方法,接下来分析AIDL原理:

服务端:因为要实现Binder,必须在服务器端创建一个Binder对象,如何创建呢?就是newAIDL接口中的Stub内部类,代码示例如:

Binder mBinder=new IBookManager.Stub(){接口方法实现}

其中IBookManager是系统根据我们自己定义的IBookManager.AIDL所生成的类。

Binder驱动:在AIDL中,Binder驱动其实就是Service。

客户端:要实现客户端跨进程和服务端通信,必须获得服务端的Binder对象在binder驱动层对应的mRemote引用,如何获得呢?首先绑定远程服务,绑定成功后的ServiceConnection中的IBinder service其实就是mRemote引用,但是因为是使用AIDL方式,所以需要在客户端中调用IBookManager.Stub.asInterface(android.os.IBinder obj)方法将服务器返回的Binder对象转换成AIDL接口,然后就可以通过这个接口去调用服务器的远程方法了。

根据原理,我们得出AIDL的使用流程,其实很简单,大致就是在服务端创建一个Service,然后创建一个Binder对象,最后在客户端得到这个Binder对象。

AIDL使用流程:
先建立AIDL,如果在你建立的AIDL接口中,有自定义的类,那么,也需要建立这个类的AIDL,并且名字要完全相同。同时在使用的时候,一定要显示的导入这个类。接下来的流程就是跟Binder的一样了。

服务器端:创建Binder对象,并且实现接口中的方法。

客户端:绑定service,得到Binder对象在驱动层对应的mRemote引用。

重点

1.当你在客户端调用服务器的方法的时候,其实是通过代理去访问,详情可以看上面的重点方法介绍里的Proxy#getBookList,所以你在客户端连续调用两次服务器的同一个方法的时候,比如,这里的getBookList,你会发现,里面的对象都不一样。因为每次在调用方法的时候,在代理类中都会创建该方法所需要的参数对象,所以里面的对象会变化。

2.AIDL中无法使用普通的接口,只能使用AIDL接口,并且实现AIDL接口的时候不能用implements,因为需要实现的接口其实是自定义接口.Stub,而不是自己定义的那个接口。使用implements无法实现。

3.解注册的时候需要使用到RemoteCallbackList,需要注意的是这个类的beginBroadcast()finishBroadcast()一定要配对使用,否则会出现异常java.lang.IllegalStateException: beginBroadcast() called while already in a broadcast,特别是在使用for循环的时候。

4.对于AIDL中的inoutinout这里就直接附上一篇别人写的博客,这篇博客讲的很详细,而且我也赞同他的观点,纸上得来终觉浅,绝知此事要躬行。

5.当使用客户端调用服务器的方法的时候,被调用的方法运行在服务器的Binder线程池中,同时客户端会被挂起,如果此时服务端方法执行耗时的话,就会导致客户端线程长时间阻塞,如果客户端线程是UI线程的话,就会导致客户端ANR,注意的是onServiceConnected(ComponentName name, IBinder service)onServiceDisconnected(ComponentName name)都运行在UI线程,所以不能在这里调用服务端耗时的方法。同理,对于服务端调用客户端的方法的情况,比如服务端调用客户端的listener中的方法的时候也是一样。即服务端挂起,方法运行在客户端的Binder线程池中。

6.当服务端因为某种异常原因停止,我们需要重新启动服务端,这里有两种方式,因为AIDL的底层是Binder,所以可以使用BinderlinkToDeathunlinkToDeath方法。还有一种方式是在onServiceDisconnected(ComponentName name)重新绑定。这两个区别就是第二种方式可以访问UI,第一种不行,因为像之前说的,onServiceDisconnected(ComponentName name)是运行在UI线程里的。而第一种方式使用的时候需要设置一个IBinder.DeathRecipient接口用于接收服务端binder因为特殊原因消失的通知,当收到通知的时候就会回调binderDied()方法,我们在这里unlinkToDeath并且重新绑定service。而这个binderDied()方法是运行在客户端的Binder线程池中的。

Messenger的原理及使用

Messenger大致的原理是这样的,因为Messenger的底层还是AIDL,所以,原理和AIDL差不多。

服务器:首先需要在服务器创建Binder对象,如何创建呢?通过Messenger来创建,所以我们需要先构造Messenger对象,对于Messenger的构造方法有两种,如下:

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

 public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

所以我们需要先构造一个Handler,这个Handler的作用其实就是处理消息。然后我们再通过这个Handler来构造Messenger对象,这个Messenger对象其实就是将客户端发送来的消息传递给Handler来处理,然后我们需要得到Binder对象,通过在ServiceonBind方法中return Messenger.getBinder(),这样就得到了Binder对象。

Binder驱动:跟AIDL一样,还是Service

客户端:也是需要得到服务端的Binder对象在binder驱动层对应的mRemote引用,获得的方式是将ServiceConnection中的IBinder service当做参数传入Messenger的构造函数中,如:

Messenger mService=new Messenger(service);

然后就可以用mService.send(msg)给服务器发消息。实现跨进程通信。
因为这里是借助Messenger,所以无法调用服务器端的方法,只能通过message来传递消息。而当服务器需要回应客户端的时候,就需要客户端提供一个Messenger,然后服务器得到这个Messenger,因为在就像客户端向服务端发送请求的时候,也是服务器提供一个Messenger,然后客户端得到这个Messenger。那么如何实现呢?因为客户端和服务器已经建立了连接,所以只需要在客户端发送消息的时候,通过消息的replyTo参数向服务器传入一个Messenger,然后服务器在接收到客户端的消息的时候得到通过messagereplyTo参数得到这个Messenger,然后利用这个向客户端发送消息就可以了。主要代码如下:
在客户端发送消息给服务器的时候:

message.replyTo=clientMessenger;

服务器接收消息的时候

 Messenger clientMessenger=msg.replyTo;

这样就在服务器端得到了客户端的Messenger,然后在服务器端通过clientMessenger.send(message);就向客户端发送了消息。

重点

1.对于使用Messenger而言,底层其实是AIDL,但是没有AIDL灵活,因为这是借助Messenger来发送消息从而进行消息的传递,不能直接调用服务端的方法,而使用AIDL是直接可以调用服务端的方法。
2.对于服务端的Messenger的作用是将客户端传递的消息传递给Handler来处理,而客户端的是发送消息给服务端。
3.Messenger是以串行的方式处理客户端发来的消息,当消息多的时候就就不合适了。而AIDL是可以并发处理客户端传来的消息。

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

推荐阅读更多精彩内容

  • Jianwei's blog 首页 分类 关于 归档 标签 巧用Android多进程,微信,微博等主流App都在用...
    justCode_阅读 5,788评论 1 23
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • Android跨进程通信IPC整体内容如下 1、Android跨进程通信IPC之1——Linux基础2、Andro...
    隔壁老李头阅读 10,564评论 13 43
  • 一、Android IPC简介 IPC是Inter-Process Communication的缩写,含义就是进程...
    SeanMa阅读 1,698评论 0 8
  • 简单说Binder Binder算是Android中比较难懂的一部分内容了,但是非常的重要,要想研究Framewo...
    EsonJack阅读 1,499评论 0 4