美文网首页
Binder札记

Binder札记

作者: 我的阿福 | 来源:发表于2020-08-17 17:26 被阅读0次

    不是很有条理,方便自己查阅

    AIDL

    总结:
    1. 其实用自己的话理解,aidl就是想办法把服务端进程的一个接口对象给到客户端,让客户端可以调用这个接口的方法实现一些进程通信功能。而这个传递通过binder方式,那么传递的对象类自然就是继承自binder,(一般来说是那个stub)要想调用自定义的方法,这个类当然也是实现了自定义的(aidl)接口(aidl里面就是接口.aidl,简单的应用内就直接通信接口)的。

    2. 还有一个比较重要的两个地方:

    • 在服务端,Stub那个对象的实现方法中,执行的时候是运行在binder的线程池中,意思就是这个方法可能会被读线程轮番调用,这里面如果涉及到全局变量的访问,如果不做线程同步的话可能会有线程安全的问题,所以要注意。
    • 客户端调用aidl接口方法的时候要注意ANR的问题,因为执行方法的时候线程会被挂起,直到服务端那头把这个方法执行完,如果这个方法比较耗时要注意。
      -------------------------------------分割线------------------------------------------------------------
    详细阐述一下

    假定叫IWork.aidl,生成的IWork.java文件中,stub是一个静态抽象类,继承了binder并实现了IWork接口,它有一个静态的子类proxy只实现了IWork接口,一般来说stub被服务端使用,proxy被客户端调用。在stub的asinterface方法中,如果在进程内使用,那么直接返回stub,如果跨进程,那么返回一个proxy。这个asinterface一般调用的地方在客户端的onserviceconnected中,传入一个binder给asinterface,得到相应的功能接口对象

    1、定义一个功能接口,如

    interface IWorkAidlInterface {
        void setWorkName(String name);
        String getWorkName();
    }
    

    这个接口将被客户端来调用,而实现端就是所谓的服务端。
    文件的名字为IWorkAidlInterface.aidl。make project会生成对应的一个IWorkAidlInterface.java文件。

    /*
     * This file is auto-generated.  DO NOT MODIFY.
     */
    package com.vcyber.testaidl;
    // Declare any non-default types here with import statements
    
    public interface IWorkAidlInterface extends android.os.IInterface
    {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.vcyber.testaidl.IWorkAidlInterface
    {
    private static final java.lang.String DESCRIPTOR = "com.vcyber.testaidl.IWorkAidlInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
    this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.vcyber.testaidl.IWorkAidlInterface interface,
     * generating a proxy if needed.
     */
    public static com.vcyber.testaidl.IWorkAidlInterface asInterface(android.os.IBinder obj)
    {
    if ((obj==null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.vcyber.testaidl.IWorkAidlInterface))) {
    return ((com.vcyber.testaidl.IWorkAidlInterface)iin);
    }
    return new com.vcyber.testaidl.IWorkAidlInterface.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_setWorkName:
    {
    data.enforceInterface(descriptor);
    java.lang.String _arg0;
    _arg0 = data.readString();
    this.setWorkName(_arg0);
    reply.writeNoException();
    return true;
    }
    case TRANSACTION_getWorkName:
    {
    data.enforceInterface(descriptor);
    java.lang.String _result = this.getWorkName();
    reply.writeNoException();
    reply.writeString(_result);
    return true;
    }
    default:
    {
    return super.onTransact(code, data, reply, flags);
    }
    }
    }
    private static class Proxy implements com.vcyber.testaidl.IWorkAidlInterface
    {
    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;
    }
    /**
         * 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);
    
    @Override public void setWorkName(java.lang.String name) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeString(name);
    mRemote.transact(Stub.TRANSACTION_setWorkName, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    @Override public java.lang.String getWorkName() 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_getWorkName, _data, _reply, 0);
    _reply.readException();
    _result = _reply.readString();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    }
    static final int TRANSACTION_setWorkName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getWorkName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    /**
         * 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);
    
    public void setWorkName(java.lang.String name) throws android.os.RemoteException;
    public java.lang.String getWorkName() throws android.os.RemoteException;
    }
    
    

    2、对于服务端,实现一个Service类,用来被客户端绑定并向其提供java文件实现的接口功能实现。这个类的onBinde方法里面返回上面那个java文件的Stub。这个Stub既继承了Binder,又实现了我们自定义的功能接口,

      IWorkAidlInterface.Stub mWorkAidlInterface = new IWorkAidlInterface.Stub() {
            private  String name;
            @Override
            public void setWorkName(String name) throws RemoteException {
                this.name =name;
            }
    
            @Override
            public String getWorkName() throws RemoteException {
                return this.name;
            }
        };
    

    3、对于客户端,这时候直接通过隐式启动,绑定服务端定义的那个Service即可,在onServiceConnected方法里面调用IWorkAidlInterface.Stub.asInterface将回调的Ibinder对象转为我们需要的功能接口对象,当然了,客户端也需要把服务端那边生成的那个java文件按照对应的包名拷贝一份才能编译。

    总结:

    Binder通信,从表面上看,其实就是在服务端的onBinder方法中返回一个类对象,这个类继承了Binder,同时实现了客户端想要调用的功能接口(AIDL通信的时候,系统通过我们定义的 功能接口.AIDL生成对应的java文件(当然也可以自己手动实现,看起来比较麻烦),有个Stub子类就是我们想要的,而进程内通信,这个类自己手动创建一个就行),当客户端绑定成功服务端的时候,拿到这个对象,当然可以转换得到想要调用的功能接口的对象,然后调用其功能。

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

    下一步研究,AIDL中,客户端调用的功能接口能否在其方法参数中添加回调?

    1、创建一个回调接口.AIDL

    // IWorkCallBack.aidl
    package com.vcyber.testaidl;
    
    // Declare any non-default types here with import statements
    
    interface IWorkCallBack {
    
        void nameCallBack(String name);
    }
    
    

    这时候注意在原来功能接口.AIDL中要明确import这个自定义的回调接口,

    ###注意这句import com.vcyber.testaidl.IWorkCallBack;
    interface IWorkAidlInterface {
        /**
         * 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);
    
        void setWorkName(String name);
        String getWorkName();
        void setCallBack( IWorkCallBack workCallBack);
    
    }
    

    然后在客户端,

      public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                    Log.e(TAG, "onServiceConnected: "+iBinder.getClass().getName());
                    workAidlInterface =IWorkAidlInterface.Stub.asInterface(iBinder);
                    try {
                      //注意这里设置回调用的是Stub而不是直接new的原接口对象
                        workAidlInterface.setCallBack(new IWorkCallBack.Stub() {
                            @Override
                            public void nameCallBack(String name) throws RemoteException {
                                Log.e(TAG, "nameCallBack: "+name );
                            }
                        });
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
    
    总结:

    在 aidl binder中,stub替代了所有需要用接口.aidl的地方,类似于一个代理。
    -------------------------------------------分割线----------------------------------------------

    下一步研究传递自定义复杂对象

    这个没有什么好说的,只是比如添加自定义类Work,先添加Work.aidl,

    package com.vcyber.testaidl;
    
    parcelable Work;
    

    在需要的地方显示导入

    package com.vcyber.testaidl;
    
    // Declare any non-default types here with import statements
    
    import com.vcyber.testaidl.IWorkCallBack;
    import com.vcyber.testaidl.Work;
    
    interface IWorkAidlInterface {
        /**
         * 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);
    
        void setWorkName(String name);
        String getWorkName();
        void setCallBack( IWorkCallBack workCallBack);
    //自定义数据类型这个方向tag必须要,一般来说直接用in就够了,
    //还有out和inout。区别也简单,参考这里:
    //定向 tag in 表示数据只能由客户端流向服务端,服务端将会收到客户端对象的完整数据,
    //但是客户端对象不会因为服务端对传参的修改而发生变动。
    //out 表示数据只能由服务端流向客户端,服务端将会收到客户端对象,该对象不为空,
    //但是它里面的字段为空,但是在服务端对该对象作任何修改之后客户端的传参对象都会同步改动。
    //inout 则表示数据可以在服务端与客户端之间双向流通。其中的数据流向是针对在客户端中的
    //那个传入方法的对象而言的。服务端将会接收到客户端传来对象的完整信息,并且客户端
    //将会同步服务端对该对象的任何变动。
        void setWork(out Work work);
         Work getWork( );
    }
    
    
     package com.vcyber.testaidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    /**
     * Created by hf on 2020-06-04.
     */
    public class Work implements Parcelable {
        private String content;
        private int time;
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        public int getTime() {
            return time;
        }
    
        public void setTime(int time) {
            this.time = time;
        }
    
        public Work(String content, int time) {
            this.content = content;
            this.time = time;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(this.content);
            dest.writeInt(this.time);
        }
    
        public Work() {
        }
    
        protected Work(Parcel in) {
            this.content = in.readString();
            this.time = in.readInt();
        }
      //还有一点需要注意就是,如果方向tag为out或者inout,那么自定义类Work需要自己添加
    //用Parcelable 插件生成实现接口代码是没有这方法的。
        public void readFromParcel(Parcel in){
            this.content = in.readString();
            this.time = in.readInt();
        }
    
    
    
        public static final Parcelable.Creator<Work> CREATOR = new Parcelable.Creator<Work>() {
            @Override
            public Work createFromParcel(Parcel source) {
                return new Work(source);
            }
    
            @Override
            public Work[] newArray(int size) {
                return new Work[size];
            }
        };
    
        @Override
        public String toString() {
            return "Work{" +
                    "content='" + content + '\'' +
                    ", time=" + time +
                    '}';
        }
    }
    
    

    写在最后:

    还有个BinderPool,相当于可以定义多个AIDL功能接口,如IWorker,ITeacher,然后再定义一个IBinderPool{IBinder getBinder(int binderType){//在这里根据binderType的值来返回客户端想要的功能的Stub}}接口,在onBinde方法中就不返回具体的AIDL功能接口的Stub而是返回IBinderPool的Stub。这样的好处是可以用一个Service同时按客户端需要返回多种类型功能接口。试了一下,一个服务Service可以被多个客户端甚至是两个应用分别调用bindService(只是不知道为何,只有第一次被绑定的时候才会执行onBind(),其他时候不执行这个方法,但这后面绑定服务的客户端却可以调用相应的接口方法,这个还要去看一下bindService的机制),这样一来这个BinderPool就比较有用了,比如对外部应用提供各种功能,直接跨进程调用,而且只需要一个Service就可以处理多个客户端的访问

    相关文章

      网友评论

          本文标题:Binder札记

          本文链接:https://www.haomeiwen.com/subject/llvbzhtx.html