Android IPC 之 AIDL 原理

在上篇文章中我们使用 AIDL 完成了进程之间的通信,下面我们分析一下 AIDL 文件的实现类

aidl 文件如下:

/**
 * 连接服务
 */
interface IConnectionService {

   oneway void connect();

    void disconnect();

    boolean isConnected();
}

实现类在 gen/aidl… 目录下面,如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.www.mk_ipc_demo;

/**
 * 杩炴帴鏈嶅姟
 */
public interface IConnectionService extends android.os.IInterface {
    /**
     * Default implementation for IConnectionService.
     */
    public static class Default implements com.www.mk_ipc_demo.IConnectionService {
        @Override
        public void connect() throws android.os.RemoteException {
        }

        @Override
        public void disconnect() throws android.os.RemoteException {
        }

        @Override
        public boolean isConnected() throws android.os.RemoteException {
            return false;
        }

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

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.www.mk_ipc_demo.IConnectionService {
        private static final java.lang.String DESCRIPTOR = "com.www.mk_ipc_demo.IConnectionService";

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

        /**
         * Cast an IBinder object into an com.www.mk_ipc_demo.IConnectionService interface,
         * generating a proxy if needed.
         */
        public static com.www.mk_ipc_demo.IConnectionService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //判断当前进程和服务进程是否为一个,如果是一个,则不需要进行 IPC 通信
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.www.mk_ipc_demo.IConnectionService))) {
                return ((com.www.mk_ipc_demo.IConnectionService) iin);
            }
            //生成 IConnectionService 服务的代理
            return new com.www.mk_ipc_demo.IConnectionService.Stub.Proxy(obj);
        }

        //返回 IBinder 对象
        @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;
             //根据传递过来的 code 来判断要触发那个方法
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_connect: {
                    //校验是不是为同一个 DESCRIPTOR
                    data.enforceInterface(descriptor);
                     //触发具体实现
                    this.connect();
                    return true;
                }
                case TRANSACTION_disconnect: {
                    data.enforceInterface(descriptor);
                    this.disconnect();
                     //disconnect 方法没有被 oneway 标记,所以需要一个返回值
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_isConnected: {
                    data.enforceInterface(descriptor);
                    boolean _result = this.isConnected();
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.www.mk_ipc_demo.IConnectionService {
            private android.os.IBinder mRemote;

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

            //返回 IBinder 对象
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }
			//返回标记
            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            //代理实现的 connect ,主进程调用时会触发的方法
            @Override
            public void connect() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    //写入标记,用于在子进程接收到这个 binder 之后能知道这个 Parcel 是来自哪个接口
                    _data.writeInterfaceToken(DESCRIPTOR);
                      //1,标识请求的是哪个方法,2,发送的数据,3,不需要返回数据,不需要等待子进程的回复
     			    //4,是一个标志位 FLAG_ONEWAY,因为我们定义的时候使用了 oneway 关键字
                    boolean _status = mRemote.transact(Stub.TRANSACTION_connect, _data, null, android.os.IBinder.FLAG_ONEWAY);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().connect();
                        return;
                    }
                } finally {
                    _data.recycle();
                }
            }

            @Override
            public void disconnect() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //3,因为没有标记 oneway,所以需要返回数据,所以传入 reply
                    boolean _status = mRemote.transact(Stub.TRANSACTION_disconnect, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().disconnect();
                        return;
                    }
                     // 因为没有标记 oneway,所以需要去读取 IPC 调用的结果,读取过程中可能会出现异常
                    _reply.readException();
                } finally {
                    //回收
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public boolean isConnected() throws android.os.RemoteException {
                //发送过去的数据是通过 _data
                android.os.Parcel _data = android.os.Parcel.obtain();
                 //返回回来的数据是通过 _reply
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    //写入标记
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_isConnected, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().isConnected();
                    }
                    _reply.readException();
                    //获取返回的数据
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static com.www.mk_ipc_demo.IConnectionService sDefaultImpl;
        }

        static final int TRANSACTION_connect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_disconnect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_isConnected = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);

        public static boolean setDefaultImpl(com.www.mk_ipc_demo.IConnectionService impl) {
            if (Stub.Proxy.sDefaultImpl == null && impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.www.mk_ipc_demo.IConnectionService getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    public void connect() throws android.os.RemoteException;

    public void disconnect() throws android.os.RemoteException;

    public boolean isConnected() throws android.os.RemoteException;
}

​ 可以看到他内部有 三个方法,分别对应着 AIDL 中定义的三个方法。还有一个默认的实现类,实现了这三个方法。这个不是重点,重点是 抽象类Stub

​ Stub 就是一个 Binder 类,实现了IConnectionService,当客户端和服务端位于同一个进程时,方法调用不会走跨进程的 transact 方法,当两者唯一不同进程时,方法调用需要走 transact 过程,这个逻辑有 Proxy来完成,在 asInterface 中就会判断是否为同一个进程,如果不是则就会返回 内部的代理类 Proxy

​ 下面介绍一下这两个类的方法和含义

DESCRIPTOR

​ Binder 的唯一标识,一般用当前类名表示

asInterface(android.os.IBinder obj)

​ 由客户端调用,用户将服务端的 Binder 对象转为客户端所需要的 AIDL 接口类型的对象。在内部判断了是否Wie同一个进程,如果是同一个进程,返回的就是服务端 Stub 对象本身,否则返回的是系统封装后的 Stub.proxy 对象,这个对象中实现了跨进程的调用

asBinder

​ 返回当前 Binder 对象

onTransact

​ 这个方法运行在服务端的 Binder 线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交给此方法处理。该方法的原型为: public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服务端通过 code 确定客户端请求的方法是啥,从 data 中取出目标方法所需要的参数(如果目标方法有参数),然后执行目标方法。目标方法执行完后就向 reply 中写入返回值(如果有返回值)。

​ 调用目标方法,这个目标方法其实就是我们在服务端实现的 Stub 的方法

​ onTransact 的执行过程就是这个样子。要注意的是如果此方法返回 false,那么客户端的请求会失败。

Proxy#isConnected

​ 这个方法执行在客户端,当客户端调用此方法时,内部实现是这样的:首先创建输入型 Parcel 对象_data,输出型 Parcel 对象_ _replay 和 返回值 boolean _result。然后把该方法的参数写入_data 中(如果有参数的话),接着调用 transact 方法来发起RPC(远程过程调用)请求,同时将当前线程挂起,然后服务端的 onTransact(就是上面的方法) 方法被调用,直到 RPC 过程结束返回后当前线程继续执行,并从 _replay 中取出 RPC 过程的返回结果。最后返回 ——reply 中的数据

Proxy#connect

​ 这个方法执行在客户端,执行过程和 isConnected 是一样的。只不过 connect 没有返回值,所以不用从_reply 中取出返回值。

需要注意的地方:

  • 发起远程请求时,当前线程会被挂起,直至服务端进程返回数据,所以这个方法时很耗时的,尽量不要在 UI 线程中发起远程请求。当然可以使用 oneway 关键字 ,使用后当前线程不会进行挂起,而是继续往下执行,但是使用 这个关键字的方法不能有返回值
  • 由于服务的的 Binder 方法运行在 Binder 的线程池中,所以 Binder 方法不管是否耗时都应该采用同步的方式去实现。

执行流程

​ 客户端 通过 asInterface 拿到 AIDL 的接口类型的对象,并且内部判断是否为同一个进程。接着使用 接口类型的对象调用 AIDL的方法,如果是不同的进程就会调用的代理 Proxy ,然后创建 发送数据和接收数据(根据方法的需求),然后调用 transact 方法发起远程调用请求,当前线程挂起,等待远程带调用结果。拿到结果后最终返回给客户端

在这里插入图片描述

参考自艺术探索

原创文章 119 获赞 77 访问量 3万+

猜你喜欢

转载自blog.csdn.net/baidu_40389775/article/details/105409865