android进程间通信Binder(二)

接上一篇文章 android进程间通信Binder(一),我们这里来看看aidl文件到底做了什么

ITest.aidl

package com.example.lenovo.testbinder;
import com.example.lenovo.bean.Student;
interface ITest {
int getAge(int num);
String getName();
Student getStudent();
}
当我们定义了一个aidl文件,android 会自动针对这个aidl文件生成一个ITest.java文件:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\alldemo\\TestBinder\\app\\src\\main\\aidl\\com\\example\\lenovo\\testbinder\\ITest.aidl
 */
package com.example.lenovo.testbinder;

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

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

        /**
         * Cast an IBinder object into an com.example.lenovo.testbinder.ITest interface,
         * generating a proxy if needed.
         */
        public static com.example.lenovo.testbinder.ITest asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.lenovo.testbinder.ITest))) {
                return ((com.example.lenovo.testbinder.ITest) iin);
            }
            return new com.example.lenovo.testbinder.ITest.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getAge: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _result = this.getAge(_arg0);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_getName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_getStudent: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.lenovo.bean.Student _result = this.getStudent();
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.lenovo.testbinder.ITest {
            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 getAge(int num) 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(num);
                    mRemote.transact(Stub.TRANSACTION_getAge, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.lang.String getName() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public com.example.lenovo.bean.Student getStudent() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.example.lenovo.bean.Student _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.example.lenovo.bean.Student.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_getStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

    public int getAge(int num) throws android.os.RemoteException;

    public java.lang.String getName() throws android.os.RemoteException;

    public com.example.lenovo.bean.Student getStudent() throws android.os.RemoteException;
}
我们来分析一下这个java文件个内容,分析完了差不多就知道这个是怎么做的了

一步步来:

我们可以看到,ITest类中里面构建了 一个内部类,Stub,继承自Binder,并且实现了ITest:

构造方法:

private static final java.lang.String DESCRIPTOR = "com.example.lenovo.testbinder.ITest";
public Stub() {
    this.attachInterface(this, DESCRIPTOR);
}

Binder.java源码

    private IInterface mOwner;
    private String mDescriptor;
    /**
     * Convenience method for associating a specific interface with the Binder.
     * After calling, queryLocalInterface() will be implemented for you
     * to return the given owner IInterface when the corresponding
     * descriptor is requested.
     */
    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

在构造方法中,把自己this和一个 DESCRIPTOR给了自己,(个人猜想,后续再寻找这个Binder对象的时候,就是根据这个DESCRIPTOR来找,找到之后,就把 mOwner返回回去,这个mOwner就是引用 )。这个DESCRIPTOR就是上文提到的包名+文件名的组合,所以在AIDL的使用的时候要求 服务端和客户端的AIDL的包名和文件名完全一致的原因,如果不一样,那么在使用和查找的时候,就没有办法去一一对应了。

        public static com.example.lenovo.testbinder.ITest asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.lenovo.testbinder.ITest))) {
                return ((com.example.lenovo.testbinder.ITest) iin);
            }
            return new com.example.lenovo.testbinder.ITest.Stub.Proxy(obj);
        }
asInterface(IBinder)方法,在客户端中,调用的就是ITest.Stub.asInterface(service)来得到的一个ITest对象,实际上调用的就是这个地方,它返回的是一个ITest.Stub.Proxy(obj),从名字上看是一个ITest的代理对象,从上面的代码中,看到了Proxy实现了ITest,那么必然的,就需要实现ITest的几个接口。

我们在Client中调用的方法就调用在这个地方来了,我截取其中一个来看看具体的实现方法中到底做了什么:

            @Override
            public int getAge(int num) 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(num);
                    mRemote.transact(Stub.TRANSACTION_getAge, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
这一块是针对Client端的数据构成:

第3、 4行定义了两个序列化对象,一个_data是用来封装Client这边传递过去的数据,一个_reply是用来封装Server端返回的数据;

第7行_data.writeInterfaceToken(DESCRIPTOR),我认为是指定binder驱动中需要把数据传递的目标,这个目标的DESCRIPTOR是“com.example.lenovo.testbinder.ITest”,这样前面构造方法中设置进去的值就有了用武之地,就避免找错了Binder引用。

第8行将数据写入到_data中

第9行就是去操作binder对象了,这里指定了操作的是哪个方法,并且将_data,_reply数据都传递过去,完成了数据的传送。

第11行从_reply中获取到服务端返回的数据值。

Binder.java源码transact:

    /**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    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方法,来看看这个方法都做了什么

@Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    switch (code) {
        ......
        case TRANSACTION_getAge: {
            data.enforceInterface(DESCRIPTOR);
            int _arg0;
            _arg0 = data.readInt();
            int _result = this.getAge(_arg0);
            reply.writeNoException();
            reply.writeInt(_result);
            return true;
        }
        ........
}
首先去确认了DESCRIPTOR,然后从_data中获取到从Client中传递过来的数据_arg0,调用this.getAge(_arg0),服务端,是TestBinder继承了ITest.Stub,所以必然是去TestBinder中找到getAge()方法,拿到返回值之后又写入了_reply中,并且return true;


我们对比客户端掌握的的transact() 和 Server端掌握的onTransact()方法,所有的数据的检查和读取,以及写入都是一一对应的,服务端writeNoExtion(),writeInt(),所以在客户端中,有了readException(),readInt()操作。


既然知道了这其中的原理,其实我们完全可以舍弃了AIDL文件来实现自己的Binder类做数据的传递和读取。

------------------------------------------------------------------------------分割线------------------------------------------------------

下面就是我自己写的Binder的实现类。

服务端:

class TestCustomBinder extends Binder implements ICustom {
    private static TestCustomBinder mBinder;

    public static TestCustomBinder instance() {
        if (mBinder == null) {
            mBinder = new TestCustomBinder();
        }
        return mBinder;
    }

    // 这个顺序是按照aidl文件中提供的方法的顺序来定义顺序的,统一从 0x01 开始
    private static final int GET_AGE = 0x01;
    private static final int GET_NAME = GET_AGE + 1;
    private static final int GET_STUDENT = GET_AGE + 2;

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case GET_AGE:
                data.enforceInterface("com.example.lenovo.testbinder.MMMMMM");
                int arg0 = data.readInt();
                reply.writeNoException();
                reply.writeInt(getCustomAge(arg0));
                return true;
            case GET_NAME:
                data.enforceInterface("com.example.lenovo.testbinder.MMMMMM");
                reply.writeNoException();
                reply.writeString(getCustomName());
                return true;
            case GET_STUDENT:
                data.enforceInterface("com.example.lenovo.testbinder.MMMMMM");
                reply.writeNoException();
                Student std = getCustomStudent();
                reply.writeInt(1);

                // 使用writeToParcel时,是直接写入的name和age数据
                // 推荐使用这种方式,在反解Parcel数据时,不需要多解释一步classname
                // reply.writeString(std.getClass().getName());
                std.writeToParcel(reply, 0);

                // 使用writeParcelable时,源码会先写入Student的类名(com.example.lenovo.bean.Student)
                // 然后调用 writeToParcel 写入数据
                // reply.writeParcelable(std, flags);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public int getCustomAge(int number) {
        return 4 * number;
    }

    @Override
    public String getCustomName() {
        return "HENSUIYI";
    }

    @Override
    public Student getCustomStudent() {
        return new Student("wangwu", 9);
    }
}

interface ICustom {
    int getCustomAge(int number);

    String getCustomName();

    Student getCustomStudent();
}
Parcel.java源码

    /**
     * Flatten the name of the class of the Parcelable and its contents
     * into the parcel.
     *
     * @param p The Parcelable object to be written.
     * @param parcelableFlags Contextual flags as per
     * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
     */
    public final void writeParcelable(Parcelable p, int parcelableFlags) {
        if (p == null) {
            writeString(null);
            return;
        }
        writeParcelableCreator(p);
        p.writeToParcel(this, parcelableFlags);
    }

    /** @hide */
    public final void writeParcelableCreator(Parcelable p) {
        String name = p.getClass().getName();
        writeString(name);
    }

客户端:

IBinder mTestBinder;
    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mTestBinder = service;
            Parcel _data = Parcel.obtain();
            Parcel _reply = Parcel.obtain();
            try {
		// 完全可以自定义,只要与服务端定义的DESCRIPTOR一致就好了。注意千万不能重名
                _data.writeInterfaceToken("com.example.lenovo.testbinder.MMMMMM"); 
                _data.writeInt(5);
                // 需要指定CODE,getAge(int) 的Code 是 0x01
                mTestBinder.transact(0x01, _data, _reply, 0);
                _reply.readException();
                int result = _reply.readInt();
                Log.e(TAG, "result = " + result);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
}

用上面这样的写法一样完成了跨进程通信,并且舍弃了AIDL文件,AIDL文件只是一种手段,简化了使用者在使用跨进程通信时的工作量,只要理解了其中的内容,一样可以完成一样的工作。

以上就是我从上层看到的流程分析,至于底层binder的内核机制,后续会去看看到底是如何完成的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值