IPC机制分析和AIDL的使用
来源:互联网 发布:c语言找出50以内质数 编辑:程序博客网 时间:2024/05/16 05:37
什么是IPC:
inter-process Communication;翻译过来就是进程间通信
首先要理解什么是进程:
进程是pc端或者移动端上的一个程序或者应用。一个进程可以包含多个线程,这是一个包含和被包含的关系。
什么时候需要使用多进程:
举个简单的例子,我们平时经常用的获取联系人,获取手机里的照片,这都是进程间的通信,只是这个通信的两个进程是我们的应用和系统的应用。再比如说,音乐播放器的后台播放,当你把显示播放信息的Activity全部杀死之后,还是能够继续播放,这时候后台实际上是还有另外一个进程在播放音乐,两个进程的生命是互补干扰的。这样就可以实现后台播放了。
还有一点就是当我们的应用需要实现的功能很复杂,需要大内存的时候,android系统为每个进程分配的内存是有限的,这时候就需要使用多进程来获得更多的内存使用
为什么会存在进程间通信?
线程间通信实现起来比进程间通信简单得多,最简单的使用一个handler就可以实现,为什么google不把handler机制使用在进程间通信上呢。这个问题我们需要从android的运行机制上分析了。android为每一个进程分配了一个独立的JVM,不同的JVM有不同的内存地址,这样会导致不同的进程访问同一个类的对象会产生新的副本,既然他们不能通过简单的内存共享来传递数据,那么必然涉及到进程间通信。
进程间通信的方式:
很多人第一答案就是AIDL,显然,AIDL是进程间通信的一种方式。但是android系统为我们提供了各种进程间通信的方式来适应不同的需要。
下面就来总结一下IPC实现的方式:
1.bundle,通过intent传递一些信息就可以实现进程间通信----这种方式适用于四大组件间的通信
2.文件共享
3.contentprovider,比如获取联系人
4.AIDL、messenger
5.socket,socket大多用于网络间的通信,功能强大,但是实现起来较复杂,需要注意的细节也很多。
说到进程间通信就必须说到非常重要的一个知识点,那就是序列化。java提供的序列化方式是serializable,android中提供的序列化方式是parcelable。这里就不进行分析了,只是需要说一下两者之间的区别。parcelable的效率是远高于serializable的,而且serializable的开销非常大,所以android上才开发了parcelable序列化方式。
还有一个就是binder,但是binder这个东西不是一时半会能讲清楚的,我只是也是一知半解。以后彻底透彻了之后再写吧
下面就说重点:AIDL的使用。
传递简单数据就不写出来了,直接写传递对象(我们自定义的model类)
服务端写法:
第一步新建aidl文件夹和新建一个包,名称一定要和java代码下放要传递的对象的类的包名一致
第二步:序列化要传递的对象类
第三步:在第一步建好的包下面新建aidl接口。注意:这里是两个aidl文件;一个aidl文件和要传递的对象类名一致,还有一个定义我们传递的接口
建好之后工程目录如下:
User.aidl文件里面只需要写一句话,下面是User.aidl的内容
parcelable User;
然后是传递接口aidl,里面内容如下:
package com.ciotea.server.model;import com.ciotea.server.model.User;// Declare any non-default types here with import statementsinterface TransformUser { void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString); boolean addUser(in User user); List<User> getUser(in String name);}
注意:其中import com.ciotea.server.model.User;是需要我们手写上去的,AS不会自动导包
addUser方法传递的参数是User,前面需要加上in,否则编译会报错。这里的in可以设置为in,out,intout;具体含义如下
in:参数由客户端设置,或者理解成客户端传入参数值。
out:参数由服务端设置,或者理解成由服务端返回值。
inout:客户端输入端都可以设置,或者理解成可以双向通信。
序列化部分就不贴出来了。自行度娘吧
接下来需要我们make project一下。AS会自动在我们的generated / source / aidl / debug / 目录下面自动生成一段Java代码
目录结构如下:
如果AS已经自动生成了这些代码。说明前面的aidl文件定义是成功的。
客户端
接下来要做的就是把aidl文件和需要传递的对象的类拷贝到客户端中,就不一一解释了,贴上结构:
只有一个需要注意的:包名要和服务端代码的包名一致,同样的make project一下。也会生成和服务器端一样的代码
接下来我们来看看AS帮我们自动生成的这个类
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\AidlDemo\\client\\src\\main\\aidl\\com\\ciotea\\server\\model\\TransformUser.aidl */package com.ciotea.server.model;// Declare any non-default types here with import statementspublic interface TransformUser extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.ciotea.server.model.TransformUser { private static final java.lang.String DESCRIPTOR = "com.ciotea.server.model.TransformUser"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.ciotea.server.model.TransformUser interface, * generating a proxy if needed. */ public static com.ciotea.server.model.TransformUser asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.ciotea.server.model.TransformUser))) { return ((com.ciotea.server.model.TransformUser) iin); } return new com.ciotea.server.model.TransformUser.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 != data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } case TRANSACTION_addUser: { data.enforceInterface(DESCRIPTOR); User _arg0; if ((0 != data.readInt())) { _arg0 = User.CREATOR.createFromParcel(data); } else { _arg0 = null; } boolean _result = this.addUser(_arg0); reply.writeNoException(); reply.writeInt(((_result) ? (1) : (0))); return true; } case TRANSACTION_getUser: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.util.List<User> _result = this.getUser(_arg0); reply.writeNoException(); reply.writeTypedList(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.ciotea.server.model.TransformUser { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public boolean addUser(User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); boolean _result; try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0); _reply.readException(); _result = (0 != _reply.readInt()); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.util.List<User> getUser(java.lang.String name) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<User> _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(name); mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(User.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; public boolean addUser(User user) throws android.os.RemoteException; public java.util.List<User> getUser(java.lang.String name) throws android.os.RemoteException;}
貌似一看,好多代码,看得脑袋都大,但是我们把Stub这个内部类收起来之后再来看:
一下就变得清晰了:一个内部类Stub,三个方法:basicTypes();addUser();getUser();
是不是有点熟悉:没错,这就是我们刚才在aidl中定义的三个方法;
我们再来一步一步看这个Stub内部类,
public static abstract class Stub extends android.os.Binder implements com.ciotea.server.TransformUser
首先,它继承自Binder类,所以它是binder子类。
它里面还有一个内部类Proxy,Proxy作为一个代理类,当位于不同的进程时就会调用proxy来执行请求
再来解释stub里面的方法:
DESCRIPTOR:binder的标示,一般用当前binder的包名+类名
asInterface(android.os.IBinder obj):传入的是一个binder对象,这个方法的作用是将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,但是它里面是做了判断的,如果是同一个进程,那么返回的就是stub对象本身,如果是不同的进程就返回Stub的代理proxy对象
asBinder():返回当前binder对象
public booleanonTransact(intcode, android.os.Parcel data, android.os.Parcel reply,intflags):
这个方法运行在服务端中,当客户端发起请求的时候,服务端会调用这个方法去做处理,
ontransact方法的第一个参数是客户端传递过来的需要调用的方法的code,服务端根据这个code判断客户端需要调用的方法
ontransact方法第二个参数是客户端传递过来的参数。
ontransact方法第三个参数是服务端回复给客户端的结果,如果是有返回值的方法,会先调用本地的方法进行查询。如果有结果,就会向reply里面序列化写入结果_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
接下来就是服务端service的编写
这里我声明了一个list集合,模仿数据库操作
service里面主要做一件事就可以了,创建一个Stub对象,在onbind方法里面返回给客户端就好,客户端拿到这个stub对象就可以进行访问了
别忘了服务要在mainfest里面注册,还需要声明action才能被其他进程访问。
<service android:name="com.ciotea.MyService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="cn.bgxt.Service.CUSTOM_TYPE_SERVICE"/> </intent-filter></service>
代码如下:
public class MyService extends Service { static List<User> users = new ArrayList<>(); static { users.add(new User("xiong",23,"北京大学")); users.add(new User("zhou",23,"人名大学")); users.add(new User("xiong",23,"中国民航大学")); users.add(new User("xiong",23,"哈佛大学")); } private Binder stub = new TransformUser.Stub() { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public boolean addUser(User user) throws RemoteException { boolean b = users.add(user); return b; } @Override public List<User> getUser(String name) throws RemoteException { List<User> reply = new ArrayList<>(); for (User user : users) { if (user.name.equals(name)){ reply.add(user); } } return reply; } }; public MyService() { } @Override public IBinder onBind(Intent intent) { Log.i("MyService","onBind"); return stub; }}再后面就是客户端绑定服务和调用服务的方法就好了
首先需要创建一个ServiceConnection对象,通过onServiceConnected 方法里面的binder对象通过asInterface就能拿到
服务端我们声明的aidl接口的对象,就能调用服务端的方法进行操作了
private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { transformUser = TransformUser.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { } };//绑定服务 Intent intent = new Intent(); intent.setAction("cn.bgxt.Service.CUSTOM_TYPE_SERVICE"); boolean b = bindService(intent, connection, BIND_AUTO_CREATE); Log.i("====",b+"");
接下来只需要调用aidl中声明的方法:
@Override public void onClick(View view) { switch (view.getId()){ case R.id.add_user: try { boolean b = transformUser.addUser(new User("周鱼儿", 23, "中山大学")); Log.i("server adduser",b+""); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.select_user: try { List<User> users = transformUser.getUser("xiong"); for (User ser : users) { Log.i("client getuser",ser.name+ser.age+ser.shcool); } } catch (RemoteException e) { e.printStackTrace(); } break; } }查询结果和添加结果
到这里一个aidl过程就完成了。其实aidl并不难,只是要细心,很多地方一个小问题就导致自动生成代码不通过。
这里就把需要注意的地方和问题总结一下:
1.包名一定要一致,aidl的包名和要传递的对象的包名要一致,客户端和服务端的aidl文件包名要一致,客户端要传递对象的包名和服务端要传递对象的包名要一致
2.当不是基本类型的传递时,需要添加一个新的aidl类,类名和传递对象一致,里面只写一句话: pareclable +传递对象的类名
3.refactor AIDL包名之后,aidl文件里面不会自动更新包名,需要收到更改。
4.aidl文件的不会自动import,需要手动import
- IPC机制分析和AIDL的使用
- IPC-AIDL的使用实例和分析
- 简单理解IPC机制和AIDL的使用
- IPC机制---使用AIDL
- Android IPC机制之AIDL的使用
- Android中RemoteService的使用详解(Aidl、IPC机制)
- IPC 主要是messenger和aidl的使用和简单分析
- 在Android使用AIDL实现IPC机制
- 使用AIDL分析Binder的工作机制
- Android AIDL IPC机制
- IPC机制<二>AIDL
- IPC机制(AIDL)
- IPC(三)---IPC实现之Messenger和AIDL的使用讲解
- Android的IPC机制(一)——AIDL的使用
- Android的IPC机制(一)——AIDL的使用
- Android的IPC机制(一)——AIDL的使用
- Android IPC之AIDL的使用
- 使用AIDL实现IPC
- 腾讯开源手游热更新方案,Unity3D下的Lua编程
- ORA-00600: internal error code, arguments: [kcblasm_1], [103], [], [], [], [], [], []
- 驱动笔试题目
- java中 << 什么意思?比如1<<30
- 关于华为无法显示调试信息和错误信息
- IPC机制分析和AIDL的使用
- _cffi_backend.so: undefined symbol: PyUnicodeUCS4_AsASCIIString
- Ceph-Radosgw开发实践之防盗链(一)
- Hibernate各种主键生成策略与配置详解
- Android之自定义view详解2
- 嵌入式 Linux 实时化之路-总览
- js next
- Android 系列 2.7设计各种APP
- 2017-1-6开通博客