Android进阶——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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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;
        }
    };

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

写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) {

        }

    };

    ......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

测试

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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119

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

  • 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);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这个方法运行在服务端中的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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/user11223344abc/article/details/80623410

猜你喜欢

转载自blog.csdn.net/janronehoo/article/details/80737274