绑定本地service并通讯
在service中定义Binder类,Binder类方法中可以访问service的信息,在service的onbind返回binder类。
activity在它的serviceconnection.onserviceconected方法中会获得onbind返回的binder对象,从而访问service信息。
所以对本地service来说,binder对象就是service返回的代理对象,客户端可以通过它反问service内部数据,从而实现客户端和service之间的通讯。
如果我们有一个接口提供服务,接口一定是抽象的,可以在service中创建实现接口的binder类,达到服务的目的,剩下的和上面的相同,返回这个binder类为客户端提供服务。
如果service想进行高消耗行为,可以继承intentservice方法,可以进行串联耗时工作,但不支持并发。
并发需要添加handle
onStartCommand返回值
如果service不在同一线程需要AIDL
首先将接口AIDL化,AIDL会自动生成java文件。
之后服务端修改binder类继承AIDL.stub,它是继承了binder类并且实现了AIDL接口。
客户端通过AIDL.Stub.asInterface(service)获得binder。
binder和service,IPC之间关系
service通信采用Ibinder通信,Ibinder也主要用于service,包含两种,一种是本地通信,另一种是多进程通信,也就是IPC。
实现Ibinder可以3种方法,继承Binder类,多进程通信可以采用AIDL和Messanger
其实AIDL也是继承Binder类。
Messenger核心,Message以及Handler。
首先服务端创建一个Handler,里面有handleMessage处理客户端
之后使用回调的Handler创建一个Messenger,Messenger(Handler){Handle.getIMessenger()}
在onbind方法中返回底层的binder Messenger.getBinder().
客户端的onServiceConnected通过传过去的Ibinder创建Messenger(Ibinder){IMessenger.Stub.asInterface(Ibinder)},之后通过Messenger传递Message。
使用的载体what,arg1,arg2,Bundle以及replyTo
如果需要服务端对客户端进行回复,客户端定义一个reHandler,利用这个Handler创建一个reMessenger,设置msg.replyto=reMessenger指定回复接收消息者,之后通过seMessenger发送信息,seMessenger收到信息,seHandle处理,通过reMessenger.send 发送
可以看出Messenger就是一个收件地址
AIDL步骤
服务端创建AIDL文件包含接口,在service中实现这些接口
客户端绑定服务端,将onServiceConnection得到的Ibinder转化为AIDL生成的Iinterface实例,通过实例调用方法。
Messenger和AIDL
Messenger串联AIDL并发
service生命周期
onUnbind返回true调用onRebind,onRebind返回空值,但可以接收Ibinder
false调用onBind
AIDL流程
AIDL创建以后客户端服务端都要有
创建的接口继承了IInterface
服务端
- 重写AIDL.Stub中的方法,具体实现BookManager.stub mBookManager = new BookManager.stub() 并在内部实现具体方法。也可以服务端新建继承Stub类
- onBind中返回AIDL.Stub
客户端
AIDL.Stub.asInterface(service)获得binder。
AIDL源码分析
客户端分析
asInterface传参Ibinder返回stub
先判定是否在同一进程,如果是,返回服务端AIDL的interface对象本身,就是Stub,否则返回Stub.proxy
proxy获取换过去的Ibinder mremote
- 生成_data _reply数据流data.writeInterfaceToken(DESCRIPOR)
- 调用mremote.transact传给服务端并请求服务端调用指定方法。调用的是Stub中的实现方法。
- 接收reply数据流
服务端分析
1获取客户端传过来数据,根据ID执行响应操作
2传过来数据data取出来,调用本地方法。data.enforceInterface(DESCRIPTOP)
3回传数据写入reply
服务端调用客户端方法
提供一个AIDL的客户端方法的接口,因为AIDL中无法使用普通接口
在客户端中对方法进行具体实现
Binder连接池
- 为连接池创建queryBinder接口
- 为连接池创建远程service,并实现queryBinder,返回需要具体的继承Stub的Binder对象
使用
- 连接service,获取Binderpoor
- 获得连接池
- 通过queryBinder获取Binder
注意事项
- onTransact返回false,客户端的请求会失效。
- 客户端线程会被挂起直至服务端进程返回数据,所以耗时方法不应在UI线程执行。
- 服务端的Binder 方法在Binder线程池中,所以应采用同步方式。
- Binder意外死亡,需要重新连接服务,两种方法,第一种给Binder设置死亡代理,linkToDeath(DeathRecipient{binderDied{unlinkToDeath}})设置死亡代理,死亡会回调客户端Binder线程池中binderDied方法。第二种方法,在onServiceDisconnected重连,在客户端中UI线程中被回调。另外可以设置isBinderAlive检查Binder是否死亡
- CopyOnWriteArrayList支持并发读写
- RemoteCallbackList 系统专门提供用于删除跨进程listener接口 使用原因 多次跨进程传输客户端的同一个对象会在服务端生成不同对象,但新生成对象底层Binder对象是同一个
- 客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中,Binder线程池可以执行大量耗时工作,如果某个远程方法是耗时的,避免在UI线程中调用。远程服务端调用客户端中方法,被调用方法运行在客户端Binder线程池中,不能访问UI内容,如果要访问客户端UI,需要使用Handler切换到UI线程
- 在AIDL中使用权限验证功能,1在onBind中进行验证,使用permission,返回null2在服务端onTransact方法中返回false,可以通过permission或UidPid验证
IPC方式
1使用Bundle,只能放入序列化数据
2使用文件共享,避免数据同步,妥善处理并发读写。不建议SharedPreferences,缓存在内存中,多进程模式下读写不可靠,会丢失数据。
3Messenger
4AIDL
5ContentProvider
底层Binder
通过ContentProvider跨进程通信
- ContentProvider六个方法,onCreat运行在主线程,其他getTypeCRUD运行在Binder线程池中,每次的线程都不一样
4Socket
流势套接字Tcp 用户数据报套接字UDP
网友评论