Android IPC—Android Studio 3.0下编写AIDL

期望效果

俩app(也就是俩进程的意思),分别为client和server(模拟binder C/S架构),server端提供的服务为一个加法计算的服务(方便理解,从最简单的入手),client端本地通过aidl远程去访问server端的服务,并得到计算结果。

先建俩工程

写Server端

1.展开server项目目录
2.创建CalcAidl.aidl:鼠标右击src文件夹new—Aidl File—>CalcAidl.aidl
3.编写CalcAidl.aidl

// CalcAidl.aidl
package zj.com.aidl_server;

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

interface CalcAidl {

    int calc(int a,int b);
}

4.clean project,确保生成build/gen/source/packagename/CalcAidl.java
5.将CalcAidl.aidl拷贝到客户端项目下,注意在客户端的包名路径要和服务端一样(重要)
6.创建并编写CalcService.java

public class CalcService extends Service {

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

    IBinder mIBinder = new CalcAidl.Stub() {
        @Override
        public int calc(int a, int b) throws RemoteException {
            return a + b;
        }
    };

}

写Client端

1.确保已经拷贝了CalcAidl.aidl在对应相同的包名路径下。并clean项目,确保gen目录下生成了CalcAidl.java
2.定义界面

3.通过绑定远程service,初始化远程iBinder实例


    ......

    private void prepareAidl() {

        Intent intent = new Intent();

        intent.setComponent(new ComponentName("zj.com.aidl_server","zj.com.aidl_server.CalcService"));

        bindService(intent,con, Context.BIND_AUTO_CREATE);
    }


    private ServiceConnection con = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mCalcAidl = CalcAidl.Stub.asInterface(service);//初始化远程iBinder实例
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

    };

    ......

4.点击计算的时候,调用远程的“计算”方法,算出结果反馈到本地界面上。

        calcbtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int num1 = Integer.parseInt(MainActivity.this.num1.getText().toString());
                int num2 = Integer.parseInt(MainActivity.this.num2.getText().toString());


                //调远程服务接口
                try {
                    int calc = mCalcAidl.calc(num1, num2);
                    result.setText("" + calc);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }
        });

测试

1.先让service端运行起来
2.再让client端运行起来
3.操作client端调用远程服务的方法进行计算

分析Binder文件

贴出上面这个demo的aidl生成的java文件。


    /**
     * This file is auto-generated.  DO NOT MODIFY.
     * Original file: D:\\android\\demo\\IPC_AIDL\\Aidl_Server\\app\\src\\main\\aidl\\zj\\com\\aidl_server\\CalcAidl.aidl
     */
    package zj.com.aidl_server;
    // Declare any non-default types here with import statements

    public interface CalcAidl extends android.os.IInterface {

        /**
         * Local-side IPC implementation stub class.
         * 这个Stub内部类实际上就是一个Binder
         */
        public static abstract class Stub extends android.os.Binder implements zj.com.aidl_server.CalcAidl {

            //Binder唯一标识,一般用当前Binder的类名表示。
            private static final java.lang.String DESCRIPTOR = "zj.com.aidl_server.CalcAidl";

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

            /**
             * Cast an IBinder object into an zj.com.aidl_server.CalcAidl interface,
             * generating a proxy if needed.
             * 用于将服务端的Binder对象准换成客户端所需要的AIDL接口类型对象,这种转换过程是区分进程的,如果客户端和服务端位于
             * 同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回系统封装后的Stub.proxy对象
             */
            public static zj.com.aidl_server.CalcAidl asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof zj.com.aidl_server.CalcAidl))) {
                    return ((zj.com.aidl_server.CalcAidl) iin);
                }
                return new zj.com.aidl_server.CalcAidl.Stub.Proxy(obj);
            }

            //此方法用于返回当前Binder对象
            @Override
            public android.os.IBinder asBinder() {
                return this;
            }


            /**
             * 这个方法运行在服务端中的Binder线程池当中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。
             */
            @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_calc: {
                        data.enforceInterface(DESCRIPTOR);
                        int _arg0;
                        _arg0 = data.readInt();
                        int _arg1;
                        _arg1 = data.readInt();
                        int _result = this.calc(_arg0, _arg1);
                        reply.writeNoException();
                        reply.writeInt(_result);
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }

            private static class Proxy implements zj.com.aidl_server.CalcAidl {
                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 int calc(int a, int b) throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    int _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        _data.writeInt(a);
                        _data.writeInt(b);
                        mRemote.transact(Stub.TRANSACTION_calc, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.readInt();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
            }

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

        public int calc(int a, int b) throws android.os.RemoteException;
    }

大致的注释我注了一下,但是我这里还是单独的将每个方法和元素拧出来说一下。

  • public static abstract class Stub extends android.os.Binder implements zj.com.aidl_server.CalcAidl { …
    Stub即Binder

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

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

  • asBinder
    此方法用于返回当前Binder对象

  • onTransact { switch (code) { … }

        @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_calc: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.calc(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

这个方法运行在服务端中的Binder线程池当中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。
方法体内有一个 “java switch (code) {… “,这个code是让其知道需要执行哪一个方法
方法体内有一个 “_arg1 = data.readInt(); “,这个data能够得到一个参数
方法体内有一个 “reply.writeInt(_result);”,这个reply携带结果并返回
另外,这个方法的返回值返回false时,那么客户端的请求会失败,我们可以利用这一特征来做权限验证,让经过我们授权的客户端才可以调用我们的服务。

  • Proxy#calc
     @Override
            public int calc(int a, int b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_calc, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

1.这个方法的运行由客户端发起
2.创建需要的Parcel对象,_data,_reply,_result,并writeInt一些无聊的小数据
3.m**Remote.transact(Stub.TRANSACTION_calc, _data, _reply, 0);**transact发起RPC(远程过程调用)
4.当前线程挂起
5.远程服务端的onTransact被调用,RPC过程返回
6.客户端挂起想成被唤醒,从_reply中取出RPC过程返回的结果,最后返回_reply中的数据

一张图感受一下

这图我截的《Android开发艺术探索》上的图

经过这一波分析和学习,我们知道,AIDL,实际上是我们用来帮助我们生成Binder的一种手段,理论上我们完全可以脱离AIDL来手写一个Binder。并且这里也只是应用并未涉及到原理。

Thanks

《android开发艺术探索》
Demo:https://github.com/zj614android/CaclAIDL

猜你喜欢

转载自blog.csdn.net/user11223344abc/article/details/80623410