端午节假期 第一天,闲下来整理下一直想总结的东西。
今天说的是进程间通讯。
1.应用场景:在android中应用是以进程的形式存在的,而在android中是不允许跨进程内存共享数据的,那如何在进程间传递对象呢?在此背景下,进程间通讯技术诞生了——Binder机制,基于此基础之上提供了整体的封装,从而实现对象代理机制。
2.aidl介绍:android接口定义语言。aidl支持的数据类型:java原子类型,Binder引用以及实现了Parcelable接口的对象。
新建一个aidl文件后(这里以ITestService为例),adt会在项目的gen目录下自动生成java文件。其主要完成:一是定义java interface,内部包含aidl文件所声明的服务函数,该类基于IInterface接口,即需要提供一个asBinder函数;二则定义一个proxy类,该类将作为客户端远程访问服务端的代理;最后则是定义一个stub类,其是基于Binder的抽象类
@Override public android.os.IBinder asBinder() { return this; } private static class Proxy implements com.demo.ITestService {//aidl接口中定义的各种方法 } /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); }
3.通讯:在这里我们称互相通信的进程双方为客户端和服务端
(1).客户端初始化与服务端之间的连接获取服务端代理对象
/* * 初始化客户端和服务器端的连接 */ private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 从远程service中获得AIDL实例化对象 tService = ITestService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.i("TAG", "disconnect"); tService = null; } };
(2).客户端通过调用服务器代理对象的方法向服务器端发送请求。
@Override public void login(final String userName, final String passWord) { new Thread(new Runnable() { @Override public void run() { try { new Thread(new Runnable() { @Override public void run() { try { if (tService == null) { Log.e("TAG", "tServicenull"); } tService.login(userName, passWord); } catch (RemoteException e) { e.printStackTrace(); } } }).start(); }
(3).代理对象把用户请求通过Linux内核的Binder驱动发送到服务端进程,服务端进程处理用户请求,并通过Linux内核的Binder驱动返回结果给客户端的服务器代理对象。
private ITestService.Stub binder = new com.demo.ITestService.Stub() { @Override public void login(final String name, final String password) throws RemoteException { //服务端登陆逻辑判断 } } /** 登录回调 */ @Override public void onLoginResult(Message message) throws RemoteException { handler.post(new Runnable() { @Override public void run() { synchronized (locker) { //登录结果处理 } }); }
备注:1.客户端与服务端的绑定方式
(一)、startService启动服务,会调用如下生命周期方法:
onCreate—>onStart—>onDestory()
(二)、bindService启动服务,会调用如下生命周期方法:
onCreate—>onBind—>onUnbind—>onDestory()
两者的不同点:使用startService()方法启用,访问者与服务之间没有关联,即使访问者退出了,服务仍然运行,使用bindService()方法启用服务,访问者与服务绑定在一起,访问者一旦退出,服务也就终止例如:final Intent service = new Intent(ITestService.class.getName());
this.app.bindService(service, connection, Context.BIND_AUTO_CREATE);
2.服务端将登陆结果如何返回给客户端中自己的代理?
这里用到了进程间通讯中的RemoteCallbackList回调技术。
客户端:
//初始化服务端回调 ITestServiceCallBack callBack = new ITestServiceCallBack.Stub() { //各种回调方法,如上面的public void onLoginResult(Message message){} } //服务端代理注册监听 tService.registerListeners(callBack);
服务端:在实现自己的各种方法的同时给客户端提供回调监听接口
private ITestService.Stub binder = new com.demo.ITestService.Stub() { @Override public void login(final String name, final String password) throws RemoteException { //服务端登陆逻辑判断 } /** * 注册回调监听 */ @Override public void registerListeners(ITestServiceCallBack callback) throws RemoteException { listeners.register(callback); }; /** * 销毁回调监听 */ @Override public void unRegisterListener(ITestServiceCallBack callback) throws RemoteException { listeners.unregister(callback); }
登录逻辑判断完成后,广播发送消息
int n = listeners.beginBroadcast(); for (int i = 0; i < n; i++) { ITestServiceCallBack listener = listeners .getBroadcastItem(i); listener.onLoginResult(message); }
3.客户端终止与服务端连接
app.stopService(new Intent(app.getApplicationContext(), TestService.class));
该知识点整理完毕,后续有新感悟会陆续修正,完善。端午节快乐~