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

public static abstract class Stub extends android.os.Binder implements com.ciotea.server.TransformUser 
0 0
原创粉丝点击