Android进程间通信的方式之AIDL

一、AIDL是什么

它全称是Android Interface Definition Language,即Android接口定义语言,为了使其他的进程也可以访问本进程提供的服务,Android使用AIDL来公开服务的接口,它里面定义了本进程可以为其他进程提供什么服务,即定义了一些方法,其他进程就可以通过RPC(远程调用)来调用这些方法,从而获得服务,其中提供服务的进程称为服务端,获取服务的进程称为客户端。

二、AIDL接口的创建

AIDL接口用来暴露服务点提供给客户端的方法,新建一个AIDL接口文件,只需要在你的项目中 点击包名 -> 右键 -> new -> AIDL -> Aidl.file,然后输入AIDL接口名称,这里我输入了IUserManager,然后点击Finish,就会在你的main目录下创建了一个aidl文件夹,aidl文件夹里的包名和java文件夹里的包名相同,里面用来存放AIDL接口文件,如下:

在里面你会发现你刚刚创建的AIDL接口IUserManager,点进去,如下:

// IUserManager.aidl
package com.example.aidltest;

// Declare any non-default types here with import statements

interface IUserManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

里面声明了一个方法,写着AIDL支持的一些数据类型(int、long、boolean、float、double、String),除了这些,AIDL还支持其他的基本数据类型、ArrayList(里面的每个元素都要被AIDL支持)、HashMap(里面的每个元素都要被AIDL支持)、实现了Parcelable接口的对象和AIDL接口本身,还有AIDL接口中只支持声明方法,不支持声明静态常量。

其中如果要在AIDL接口文件中使用AIDL对象,必须显式的 import 进来,即使它们在同一个包内,还有如果在AIDL接口文件用到了Parcelable对象,必须新建一个和它同名的AIDL文件,并在其中声明它为parcelable类型,接下来我要使用User这个Parcelable对象,所以我要在aidl文件夹下新建一个和他同名的AIDL文件,如下:

然后在User.aidl中添加如下内容:

// User.aidl
package com.example.aidltest;

parcelable User;

在里面,我声明了User.java这个对象为parcelable类型,接下来把IUserManager中的basicTypes方法删除,添加一个根据用户姓名获得用户信息的方法,如下:

// IUserManager.aidl
package com.example.aidltest;

import com.example.adiltest.User;

interface IUserManager {

   User getUser(String name);
}

在里面我显示的 import 了User这个AIDL文件,即使它们在同一个包内,并声明了一个getUser方法,这个方法将会在服务端实现,然后在客户端调用(RPC)。

三、根据AIDL接口生成 对应的Binder类

有了AIDL接口后我们需要根据AIDL接口生成客户端和服务端对应的Binder类,有两种方式生成,一种是通过SDK自动生成,另外一种是我们自己手动编码实现,其中能够进行手动编码实现的前提是基于对SDK自动生成的各种Binder类的充分理解,下面我们先来介绍SDK自动生成的Binder类。

3.1、SDK自动生成

我们在AS导航栏 Build -> ReBuild Project,SDK就会替我们在 app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\包名 下生成一个IUserManager.java,它就是根据IUserManager.aidl文件生成的,里面没有缩进,所以看起来不习惯,使用快捷键ctrl+alt+L,格式化一下代码,如下:

//IUserManager.java

/**
 * 1、IUserManager接口,getUser方法定义在其中
 **/
public interface IUserManager extends android.os.IInterface {
   
    /**
     * 1、抽象类Stub,需要在远程服务端实现
     */
    public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IUserManager {
        private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IUserManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        
        public static com.example.aidltest.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.aidltest.IUserManager))) {
                return ((com.example.aidltest.IUserManager) iin);
            }
            return new com.example.aidltest.IUserManager.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_getUser: {//case TRANSACTION_getUser分支
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    //调用getUser方法的具体实现
                    com.example.aidltest.User _result = this.getUser(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        /**
         * 2、代理类Proxy,客户端使用
         */
        private static class Proxy implements com.example.aidltest.IUserManager {
            
            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 com.example.aidltest.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();
                com.example.aidltest.User _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(name);
                    //传进了TRANSACTION_getUser字段
                    mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.example.aidltest.User.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    
 	public com.example.aidltest.User getUser(java.lang.String name) throws android.os.RemoteException;
}

这个文件有3个主要的类:

1、IUserManager接口

它声明了IUserManager.aidl定义的getUser方法,继承自IInterface。

2、IUserManager.Stub抽象类

IUserManager的静态内部类,它继承自Binder,说明它是一个 Binder 本地对象(Binder下面介绍),它虽然实现了IUserManager接口,但是它继续声明为一个抽象类,并没有实现IUserManager接口中的getUser方法,表明子类需要实现getUser方法,返回具体的User信息,而服务端将会实现这个Stub抽象类。

**3、IUserManager.Stub.Proxy代理类 **

IUserManager.stub的静态内部类,它实现了IUserManager接口,并且实现了getUser方法,但是里面只是把数据装进data这个Parcel对象,通过mRemote的transact方法发送给服务端,接着用reply这个Parcel对象等待服务端数据的返回,这一切都是通过mRemote这个IBinder对象进行,mRemote代表着Binder对象的本地代理(IBinder下面介绍),mRemote会通过Binder驱动来完成与远程服务端的Stub的通信。

可以看到Stub类和Stub.Proxy类都实现了IUserManager接口,这就是一个典型的代理模式,它们的getUser方法有着不同的实现,Stub类它将会在远程的服务端完成getUser方法的具体实现,而Stub.Proxy类是本地客户端的一个代理类,它已经替我们默认的实现了getUser方法,该方法里面通过mRemote这个Binder引用的transact方法把请求通过驱动发送给服务端,我们注意到mRemote发送请求时还传进了TRANSACTION_getUser这个代表着getUser方法的标识名,这表示客户端告诉服务端我要调用getUser这个方法,当驱动把请求转发给服务端后,服务端的Stub类的onTransact方法就会回调,它里面有一个switch语句,根据code来调用不同的方法,这时它就会走到case TRANSACTION_getUser这个分支,然后调用getUser方法的在服务端的具体实现,如果有返回值的话,还会通过reply返回给客户端,这样就通过Binder驱动完成了一次远程方法调用(RPC)。

这里要注意的是客户端通过mRemote的transact方法把请求发送给客户端之后,这时会阻塞UI线程等待服务端的返回,而服务端的onTransact方法回调时,服务端的getUser方法会被回调,这时服务端的getUser方法是运行在服务端Binder线程池中,所以如果此时有UI操作需要回到UI线程再进行UI操作。

我们还注意到IUserManager接口继承了IInterface,IUserManager.Stub继承自Binder,它们是干什么的?我们来认识一下:

IInterface

这是一个接口,用来表示服务端提供了哪些服务,如果服务端需要暴露调用服务的方法给客户端使用,就一定要继承这个接口,它里面有个asBinder方法,用于返回当前的Binder对象。

IBinder

这时一个跨进程通信的Base接口,它声明了跨进程通信需要实现的一系列抽象方法,实现了这个接口就说明可以进行跨进程通信,Binder和BinderProxy都继承了这个接口。

Binder

代表的是 Binder 本地对象(Binder实体),它继承自IBinder,所有本地对象都要继承Binder,Binder中有一个内部类BinderProxy,它也继承自IBinder,它代表着Binder对象的本地代理(Binder引用),Binder实体只存在于服务端,而Binder引用则遍布于各个客户端。

接下来我们动手实践一下,首先在服务端RemoteService中,我们要这样做:

public class RemoteService extends Service {

    //1、实现Stub类中的getUser方法
    private IUserManager.Stub mBinder = new IUserManager.Stub() {
        @Override
        public User getUser(String name) throws RemoteException {
            //这里只是简单的返回了一个用户名为name,密码为123456的用户实例
            return new User(name, "123456");
        }
    };

    public RemoteService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        //2、在onBinder方法中返回Stub类的实现,Stub类继承自Binder,Binder实现了IBinder,这样返回是没问题的
        return mBinder;
    }
}

在服务端我们需要实现Stub类中的getUser方法,然后在onBinder方法中返回Stub类的实现,这样客户端绑定服务时就会收到这个Stub类的Binder引用。

然后在客户端ClientActivity中,我们要这样做:

public class ClientActivity extends AppCompatActivity {

    private static final String TAG = ClientActivity.class.getSimpleName();

    //1、创建ServiceConnection
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected, 与服务端连接成功!");
            
            //把服务端返回的Binder引用,通过Stub.asInterface方法包装成本地代理类IUserManager.Stub.Proxy,Proxy类实现了IUserManager,所以这样写是没问题的
            IUserManager userManager = IUserManager.Stub.asInterface(service);
            
            try {
                //通过本地代理对象远程调用服务端的方法
                User user = userManager.getUser("rain");
                
                Log.d(TAG, "onServiceConnected,向服务端获取用户信息成功,User = ["
                        + "name = " + user.getName()
                        + "password = " + user.getPassword());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected, 与服务端断开连接");

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //2、启动并通过ServiceConnection绑定远程服务
        bindService(
                new Intent(this, RemoteService.class),
                mServiceConnection,
                Context.BIND_AUTO_CREATE
        );
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //3、解绑服务
        unbindService(mServiceConnection);
    }
}

在服务端我们首先要创建ServiceConnection,然后通过ServiceConnection来绑定RemoteService,在成功绑定后,ServiceConnection的onServiceConnected方法就会回调,它的第二个输入参数就是RemoteService在onBind方法返回的Stub类的Binder引用,我们拿到这个引用后,就可以通过通过Stub.asInterface方法转换为本地代理类Stub.Proxy,然后调用它的getUser方法,Proxy的getUser方法会远程调用RemoteService的getUser方法,方法返回后,在log中打印出User的信息,最后,活动结束,我们记得解绑服务,这个过程和上面介绍的一次RPC过程是一样的。

我们发现完成一次进程间的通信是非常的简单,这就好像只是简单的调用一个对象的方法,但其实这都得益于Binder在底层为我们做了更多工作,我们上层使用得有多简单,它得底层实现就有多复杂,上面的一次进程间通信,可以简单的用下图表示:

其中ServiceManager就是根据Binder的名字查找Binder引用并返回,如果你对Binder的通信架构有一定的了解,理解这个就不难,对象转换就是完成Binder实体 -> Binder引用,Binder引用 -> Binder实体的转换,在java层继承自Binder的都代表Binder本地对象,即Binder实体,而Binder类的内部类BinderProxy就代表着Binder对象的本地代理,即Binder引用,这两个类都继承自IBinder, 因而都具有跨进程传输的能力,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。

我们重点讲一下图中的第3步:将Binder引用赋值给Proxy的mRemote字段,Proxy就是前面介绍的Stub.Proxy,所以接着我们看看**IUserManager.Stub.asInterface(IBinder)**方法是如何把服务端返回的Binder引用赋值给本地的代理类Proxy的mRemote字段,asInterface方法如下:

//IUserManager.Stub.java
public static com.example.aidltest.IUserManager asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    //根据DESCRIPTOR调用IBinder的queryLocalInterface方法
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.example.aidltest.IUserManager))) {
        return ((com.example.aidltest.IUserManager) iin);
    }
    return new com.example.aidltest.IUserManager.Stub.Proxy(obj);
}

//Binder
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if (mDescriptor != null && mDescriptor.equals(descriptor)) {
        //mOwner等于Stub类实例
        return mOwner;
    }
    return null;
}

//Binder#BinderProxy
public IInterface queryLocalInterface(String descriptor) {
    return null;
}

可以发现它里面根据DESCRIPTOR标识调用IBinder的queryLocalInterface方法在查找一些什么,DESCRIPTOR是什么?DESCRIPTOR是Binder实体的唯一标识,一般用当前的Binder的类名表示,它定义在Stub类中,如本文的Stub的 DESCRIPTOR = "com.example.aidltest.IUserManager",前面已经讲过Binder和BinderProxy都继承自IBinder,所以它们的queryLocalInterface有不同的实现,我们看到BinderProxy的直接返回null;而Binder的需要和自己的DESCRIPTOR比较,如果相同就返回mOwner,否则返回null,其中mOwner就等于Stub类实例,在Stub类构造的时候赋值,如下:

public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IUserManager {
    
    private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IUserManager";

    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }
    
    //...
}

这说明了如果queryLocalInterface的返回不为空,iin != null,表示obj是Binder实体(Stub的子类),客户端和服务端在同一个进程,asInterface方法返回的就是Binder实体;如果queryLocalInterface的返回为空,iin == nul,表示obj实际上是Binder引用,客户端和服务端在不同的进程,asInterface构造一个Proxy对象返回,并把Binder引用通过构造传了进去,我们看Proxy的构造函数,如下:

private static class Proxy implements com.example.aidltest.IUserManager {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }
    
    //...
}

可以看到,传进去的Binder引用赋值给了mRemote字段,所以Proxy中的mRemote就是Binder引用,客户端就是通过这个mRemote来和服务端通信。

也就是说,如果在同一个进程,asInterface返回的是Stub的实现类,因为不存在跨进程调用,直接调用该方法就行,如果在不同进程,asInterface返回的是Proxy对象,客户端调用Proxy中的同名方法,通过mRemote的transact方法挂起当前线程等待服务端返回,服务端收到请求后响应返回数据。

到这里,相信大家在SDK把帮助下已经会使用AIDL来完成简单的进程间通信,接下来通过手动编码实现。

3.2、手动编码实现

我们发现使用AIDL系统会自动的帮我们生成上述代码,是为了方便我们的开发,系统根据AIDL文件生成的java文件格式是固定,我们完全可以抛开AIDL直接手写对应的Binder类,下面我们本着单一原则把原本IUserManager.java的里面的Stub类和Stub类中的Proxy类独立出来,所以我们总共要写3个类,分别是:IUserManager、Stub、Proxy。

1、声明一个继承自IInterface的接口

声明一个继承自IInterface的接口,在里面定义我们想要让客户端调用的方法,如下:

public interface IUserManager extends IInterface {
    User getUser(String name);
}

2、声明服务端的Stub类

Stub类需要继承Binder,表明它是一个Binder本地对象,它还需要实现IUserManager接口,但是继续声明为抽象类,不需要实现IUserManager的getUser方法,接着我们做以下几步:

1、在里面定义一个字符串DESCRIPTOR,表示Binder实体的唯一标识,用当前的Stub类的类名表示,并把它的可见修饰符改为public,待会在Proxy需要用到.

2、在构造函数中把this 和 DESCRIPTOR字符串 attach 给父类Binder中的mOwner和mDescriptor字段.

3、定义一个TRANSACTION_getUser整型数值代表着getUser方法的标识名,赋值格式照抄自动生成的Stub类的TRANSACTION_getUser.

4、定义一个asInterface静态方法,里面的内容实现照抄自动生成的Stub类的asInterface方法,需要注意里面的IUserManager接口需要换成我们刚刚定义的IUserManager接口.

5、最后重写IInterface的asBinder方法和Binder的onTransact方法,里面的内容实现照抄自动生成的Stub类的asBinder和onTransact方法.

最终这个Stub类如下:

public abstract class Stub extends Binder implements IUserManager {

    public static final String DESCRIPTOR = "com.example.aidltest.Stub";//这里改成com.example.aidltest.Stub

    static final int TRANSACTION_getUser = (IBinder.FIRST_CALL_TRANSACTION + 0);

    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static IUserManager asInterface(android.os.IBinder obj) {//导入我们自定义的IUserManager
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof IUserManager))) {
            return (IUserManager) iin;
        }
        return new 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_getUser: {
                data.enforceInterface(descriptor);
                java.lang.String _arg0;
                _arg0 = data.readString();
                com.example.aidltest.User _result = this.getUser(_arg0);
                reply.writeNoException();
                if ((_result != null)) {
                    reply.writeInt(1);
                    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }
}

3、声明客户端的Proxy类

Proxy类只需要实现IUserManager接口,并且实现IUserManager中的getUser方法,接着我们做以下几步:

1、定义一个IBinder类型的mRemote字段,并在构造函数中赋值.

2、实现IUserManager中的getUser方法和IInterface的asBinder方法,里面的内容实现照抄自动生成的Stub.Proxy的getUser方法和asBinder方法,需要注意getUser中的一些字段需要导入我们刚刚在Stub类中定义的字段.

最终这个Proxy类如下:

public class Proxy implements IUserManager {

    private IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    @Override
    public android.os.IBinder asBinder() {
        return mRemote;
    }

    @Override
    public com.example.aidltest.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();
        com.example.aidltest.User _result;
        try {
            _data.writeInterfaceToken(Stub.DESCRIPTOR);//这里引用我们自己写的Stub的DESCRIPTOR
            _data.writeString(name);
            mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);//这里引用我们自己写的Stub的TRANSACTION_getUser
            _reply.readException();
            if ((0 != _reply.readInt())) {
                _result = com.example.aidltest.User.CREATOR.createFromParcel(_reply);
            } else {
                _result = null;
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

}

最终整个工程目录如下:

4、使用

使用就很简单了,只需要把上面自动生成的IUserManager、IUserManager.Stub、IUserManager.Stub.Proxy替换成我们自己写的IUserManager、Stub、Proxy就行,就不再贴代码了,输出如下:

四、学完AIDL能干什么

在android8.0之后Activity的有关进程间的通信都是通过AIDL来实现,Android根据IActivityManager.aidl文件来生成进程间通信所需的Binder类,如ApplicationThread在AMS的本地代理,AMS在ActivityThread的本地代理,当我们通过startActiivty发起Activity的启动请求时,ActivityThread就会通过AMS本地代理调用AMS的相应方法,当Actiivty在AMS准备好后,AMS就会通过ActivityThread本地代理回调应用进程的Activity的生命周期方法,这里就不在多述了,这个过程如下:

主要是通过这个例子说明,学习完AIDL后,能够帮助我们更好的理解系统源码有关跨进程的一些术语,类等,通过AIDL也能更好的加深我们对Android进程间通信的原理的理解,也掌握了一种进程间通信的方式。

好了,文章到这里就结束了如果你觉得文章还算有用的话,不妨把它们推荐给你的朋友。

漫漫开发之路,我们只是其中的一小部分……只有不断的学习、进阶,才是我们的出路!才跟得上时代的进步!

今年年初我花一个月的时间收录整理了一套知识体系,如果有想法深入的系统化的去学习的,可以私信我【学习】,我会把我收录整理的资料都送给大家,帮助大家更快的进阶。

发布了289 篇原创文章 · 获赞 30 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_45365889/article/details/102696348