跨进程通信AIDL

1 篇文章 0 订阅
1 篇文章 0 订阅

AIDL是什么,作用是什么

项目中涉及到两个应用即两个进程间进行数据通信,而Android中进程之间的内存地址是相互独立的,一个进程无法访问另一个进程的内存地址。这里简单介绍下进程的概念,进程是一个程序或者应用的实体,每个进程都拥有自己独立的内存地址空间。官方文档上讲,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供您操作的对象。编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL来完成跨进程通信。

Android中的跨进程其实就是IPC(Inter-Process Communication)通信,而AIDL正是IPC通信的机制。
AIDL全称是Android Interface Definition Language,是安卓接口定义的意思,通过定义相关的接口来实现跨进程通信。


AIDL使用

1.AIDl支持的数据类型
AIDL 使用一种简单语法,允许您通过一个或多个方法(可接收参数和返回值)来声明接 口。参数和返回值可为任意类型,甚至是 AIDL 生成的其他接口。
您必须使用 Java 编程语言构建 .aidl 文件。每个 .aidl 文件均须定义单个接口,并且只需要接口声明和方法签名。
默认情况下,AIDL 支持下列数据类型:
Java 编程语言中的所有原语类型(如 int、long、char、boolean 等java基本数据类型)
1.String
2.CharSequence
3.List
List 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。您可选择将 List 用作“泛型”类(例如,List)。尽管生成的方法旨在使用 List 接口,但另一方实际接收的具体类始终是 ArrayList。
4. Map
Map 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。不支持泛型 Map(如 Map<String,Integer> 形式的 Map)。尽管生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap。
5.其他 AIDL 生成的接口
6.实现 Parcelable 的类数据类型

2.编写AIDL相关代码
编写服务端代码
创建实体类Person类,实现Parcelable接口,用于跨进程通信的实体类。

public class People implements Parcelable {
            private String id;
            private String name;

   @Override
   public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(id);
            dest.writeString(name);
    }

    @Override
    public int describeContents() {
           return 0;
    }

    public static final Creator<People> CREATOR = new Creator<People>() {
        @Override
        public People createFromParcel(Parcel in) {
             People people =  new People();
             people.setId(in.readString());
             people.setName(in.readString());
             return people;
        }

        @Override
        public People[] newArray(int size) {
             return new People[size];
        }
    };

    public String getId() {
         return id;
    }

    public void setId(String id) {
         this.id = id;
    }

    public String getName() {
         return name;
    }

    public void setName(String name) {
         this.name = name;
    }
}

创建.aldl文件,这里包括两个文件,一个是实体类People的文件,一个是接口文件

// People.aidl
package com.uniubi.aidlapplication;

parcelable People;
  // People.aidl
package com.uniubi.aidlapplication;
import com.uniubi.aidlapplication.People;

interface IRemoteService {
   void basicTypes(int a, long b, in People person, in String d);
}

这里要记住需要导入People类,否则.aidl文件会找不到。

实现接口

点击android studio的make project
会在build/generated//Users/chenxuming/generated/aidl_source_output_dir/debug/compileDebugAidl/out/目录下生成IRemoteService.java文件。
在这里插入图片描述
记住一定要在当前moudle的gradle文件里指定.aidl文件路径,否则编译会找不到People这个类,
在这里吃了个大亏。

sourceSets {
       main {
           manifest.srcFile 'src/main/AndroidManifest.xml'
           java.srcDirs = ['src/main/java', 'src/main/aidl']
           resources.srcDirs = ['src/main/java', 'src/main/aidl']
           aidl.srcDirs = ['src/main/aidl']
           res.srcDirs = ['src/main/res']
           assets.srcDirs = ['src/main/assets']
       }
   }

查看生成的java文件,可以看出接口IRemoteService类继承android.os.IInterface接口,里面包含了一个继承Binder的Sub子类,并且这个Sub子类实现IRemoteService接口,后面我们将会具体讲讲这个生成的IRemoteService类具体源码。

创建Service,并在其中创建binder对象,通过onBind方法返回这个binder对象,到此为止服务端代码编写完成。

public class RemoteService extends Service {

  private static final String TAG = "RemoteService";
  private IRemoteService.Stub binder = new IRemoteService.Stub() {

       @Override
       public void basicTypes(int a, long b, People person, String d) throws RemoteException {
           Log.i(TAG, "basicTypes" + person.getName());
       }
   };

   @Override
   public IBinder onBind(Intent intent) {
       return binder;
   }
}

向客户端公开接口
1.将服务端src/main下面的整个aidl目录下的文件拷贝到客户端相同的位置,在客户端项目下同样别忘记了在当前module下的gradle配置sourceSets,能让.aidl文件找到People类,然后make project。
2.绑定远程服务,这里必须是绑定远程服务,“com.uniubi.aidlapplication.RemoteService”是远程服务的actiob name,通过隐式的方式绑定。

 Intent intent1 = new Intent();
 intent1.setComponent(new ComponentName("com.uniubi.aidlapplication", "com.uniubi.aidlapplication.RemoteService"));
 bindService(intent1, serviceConnection, BIND_AUTO_CREATE);
private IRemoteService iRemoteService;

private ServiceConnection serviceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("MainActivity", "onServiceConnected");
            iRemoteService = IRemoteService.Stub.asInterface(service);

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i("MainActivity", "onServiceDisconnected");
        }
    };

通过代码可以看出绑定远程服务成功后,能拿到IBinder对象,然后通过IRemoteService.Stub.asInterface(service)转成我们需要的iRemoteService对象,然后通过
iRemoteService对象,就可以进行跨进程请求了。

 try {
       if(iRemoteService != null){
          iRemoteService.basicTypes(1, 2, people, "呵呵");
       }
 } catch (RemoteException e) {
        Log.i("MainActivity", e.getMessage());
         e.printStackTrace();
}

AIDL源码分析
生成的代码格式比较乱,最好先格式化下方便阅读。

 /*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/chenxuming/AndroidStudioProjects/AIDLClient/app/src/main/aidl/com/uniubi/aidlapplication/IRemoteService.aidl
 */
package com.uniubi.aidlapplication;

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.uniubi.aidlapplication.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.uniubi.aidlapplication.IRemoteService";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.uniubi.aidlapplication.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.uniubi.aidlapplication.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.uniubi.aidlapplication.IRemoteService))) {
                return ((com.uniubi.aidlapplication.IRemoteService) iin);
            }
            return new com.uniubi.aidlapplication.IRemoteService.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 {
            java.lang.String descriptor = DESCRIPTOR;
            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();
                    com.uniubi.aidlapplication.People _arg2;
                    if ((0 != data.readInt())) {
                        _arg2 = com.uniubi.aidlapplication.People.CREATOR.createFromParcel(data);
                    } else {
                        _arg2 = null;
                    }
                    java.lang.String _arg3;
                    _arg3 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.uniubi.aidlapplication.IRemoteService {
            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 a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) 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(a);
                    _data.writeLong(b);
                    if ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    _data.writeString(d);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void basicTypes(int a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) throws android.os.RemoteException;
}

不难发现IRemoteService这个类由两部分组成,一部分是声明的接口,一部分是继承Binder类的Sub内部类,这个Sub内部类有两个主要的方法,asInterface() 和onTransact()方法,还有个内部类Proxy,一看名字就知道是代理模式,这里就不细说代理模式了。
一步步来,首先看下iRemoteService = IRemoteService.Stub.asInterface(service)这行代码,将服务端的binder对象解析成客户端的iRemoteService对象,该方法返回的是IRemoteService.Stub.Proxy对象,也就是说iRemoteService对象就是IRemoteService.Stub.Proxy,然后再看调用 iRemoteService.basicTypes(1, 2, people, “呵呵”)的地方,其实看的就是Proxy对象的basicTypes方法,看到关键代码mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);是通过mRemote对象的transact方法来进行通信,mRemote对象就是远程服务端的Sub对象,点进去查看源码,主要查看onTransact方法,而上面也讲到Sub类里是实现了Binder类的onTransact方法的(上面已贴出来,这里不重复贴出)。

 public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

发现onTransact是通过code来指定具体调用行为,关键代码this.basicTypes(_arg0, _arg1, _arg2, _arg3);而这里的this就是远程服务端的Sub对象,具体basicTypes方法实现得去服务端看,客户端是看不到。到了这里整个客户端服务端跨进程通信的流程讲解完毕。


总结

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

2.onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
客户端跨进程请求主要是通过远程服务binder对象的onTransact方法完成。
code : 确定客户端请求的目标方法是什么。
data : 如果目标方法有参数的话,就从data取出目标方法所需的参数。
reply : 当目标方法执行完毕后,如果目标方法有返回值,就向reply中写入返回值。
flag : 0

3.DESCRIPTOR
Binder的唯一标识,一般用当前AIDL接口名表示。

总之一切都是通过binder对象来完成,远程服务端持有binder对象实体,本地客户端Proxy持有服务端binder对象引用,并通过Sub.asInterface()方法转换成本地的接口类对象。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值