通过AIDL分析Binder机制

  • 直观来说,Binder是Android中的一个类,它继承了IBinder接口。从IPC角度来说, Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它 的设备驱动是/dev/binder,该通信方式在Linux中没有;从Android Framework角度来说, Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和 相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的 媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通 过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通 服务和基于AIDL的服务。

  • Android开发中,Binder主要用在Service中,包括AIDL和Messenger,其中普通Service 中的Binder不涉及进程间通信,所以较为简单,无法触及Binder的核心,而Messenger的底 层其实是AIDL,所以这里选择用AIDL来分析Binder的工作机制。为了分析Binder的工作 机制,我们需要新建一个AIDL示例,SDK会自动为我们生产AIDL所对应的Binder类,然 后我们就可以分析Binder的工作过程。

首先创建service端:以传输Parcelable对象为例子
新建Person.java类

package com.demo.aidlservice;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable {
    private String name;
    private String age;

    public Person(String name, String age) {
        this.name = name;
        this.age = age;
    }

    protected Person(Parcel in) {
        name = in.readString();
        age = in.readString();
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(age);
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

创建aidl文件夹包名必须和包名一致,在aidl文件夹创建一个aidl文件

// IMyAidlInterface.aidl
package com.demo.aidlservice;

// Declare any non-default types here with import statements
import com.demo.aidlservice.Person;

interface IMyAidlInterface {
    List<Person> add(in Person person);
}

因为Person是在java类中,IMyAidlInterface 中并不能识别,所以要创建Person.aidl文件

// IMyAidlInterface.aidl
package com.demo.aidlservice;

parcelable Person;

很简单就这一行代码就够了。
最后创建一个service提供给客户端绑定记得要在manifests里注册service


public class IRemoteService extends Service {

    private ArrayList<Person> personList;
    private static final String TAG = "IRemoteService";

    @Override
    public IBinder onBind(Intent intent) {
        personList = new ArrayList<>();
        return iBinder;
    }

    private IBinder iBinder = new IMyAidlInterface.Stub() {
        @Override
        public List<com.demo.aidlservice.Person> add(com.demo.aidlservice.Person person) throws RemoteException {
            personList.add(person);
            return personList;
        }
    };
}

服务端的结构如下图
在这里插入图片描述

  • 接下来是客户端的代码
    在这里插入图片描述
    如图所示,创建了和服务端包名一致的aidl来存放两个aidl文件。里面的内容也是完全一样的。然后再看一下MainActivity

public class MainActivity extends AppCompatActivity {
    private IMyAidlInterface iMyAidlInterface;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iMyAidlInterface = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService();
    }

    private void bindService() {
        //禁止使用隐式服务启动服务 只能用显式Intent启动绑定服务
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.demo.aidlservice", "com.demo.aidlservice.IRemoteService"));
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }

    public void add(View view) {
        try {
            List<Person> persons = iMyAidlInterface.add(new Person("ludi", "34"));
            Log.e("persons", persons.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }
}

初始化的时候绑定service 传入了Context.BIND_AUTO_CREATE,绑定的时候自动创建Service。并且绑定service 5.0以后不允许使用隐式调用方式。点击add的时候调用到服务端add方法,然后返回集合数据。

  • 接下来分析一下整体数据的传递逻辑。
    首先,当我们编译的系统会生成对应的java类,下面是服务端生成的IMyAidlInterface.java,为了看着方便我格式化了一下代码
package com.demo.aidlservice;

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

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

        /**
         * Cast an IBinder object into an com.demo.aidlservice.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.demo.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.demo.aidlservice.IMyAidlInterface))) {
                return ((com.demo.aidlservice.IMyAidlInterface) iin);
            }
            return new com.demo.aidlservice.IMyAidlInterface.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_add: {
                    data.enforceInterface(descriptor);
                    com.demo.aidlservice.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.demo.aidlservice.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    java.util.List<com.demo.aidlservice.Person> _result = this.add(_arg0);
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.demo.aidlservice.IMyAidlInterface {
            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 java.util.List<com.demo.aidlservice.Person> add(com.demo.aidlservice.Person person) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.demo.aidlservice.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.demo.aidlservice.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public java.util.List<com.demo.aidlservice.Person> add(com.demo.aidlservice.Person person) throws android.os.RemoteException;
}

它继承了android.os.IInterface 接口,同时它也还是个接口,所有可以在Binder中自由传输的接口都需要继承IInterface接口。通过它我们可以分析Binder的工作机制,首先它声明了add()方法这个就是我们在.aidl文件中声明的方法,同时对这个方法声明了一个id来标识这个方法,用id来标识在transact过程中标记客户端到底请求了是哪个方法。接着它声明了内部类Stub,继承android.os.Binder,当客户端和服务端是同一个进程时,不会走跨进程的transact过程。当我们通过asInterface获取IMyAidlInterface的时候

public static com.demo.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.demo.aidlservice.IMyAidlInterface))) {
                return ((com.demo.aidlservice.IMyAidlInterface) iin);
            }
            return new com.demo.aidlservice.IMyAidlInterface.Stub.Proxy(obj);
        }

返回的return new com.demo.aidlservice.IMyAidlInterface.Stub.Proxy(obj);所以整个transact过程是有Stub的内部代理Proxy完成的。接下来介绍下每个方法的含义。

//Binder唯一标识,一般用当前Binder类名表示。
private static final java.lang.String DESCRIPTOR = "com.demo.aidlservice.IMyAidlInterface";
//用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过 程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的 Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。
   public static com.demo.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.demo.aidlservice.IMyAidlInterface))) {
                return ((com.demo.aidlservice.IMyAidlInterface) iin);
            }
            return new com.demo.aidlservice.IMyAidlInterface.Stub.Proxy(obj);
        }
//asBinder返回当前Binder对象
 private android.os.IBinder mRemote;
 @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }
这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求 会通过系统底层封装后交由此方法来处理。该方法的原型为public Boolean onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)。服务端通过code可以确定客户 端所请求的目标方法是什么,接着从data中取出目标方法所需的参数(如果目标方法有参 数的话),然后执行目标方法。当目标方法执行完毕后,就向reply中写入返回值(如果 目标方法有返回值的话),onTransact方法的执行过程就是这样的。需要注意的是,如果 此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证, 毕竟我们也不希望随便一个进程都能远程调用我们的服务。

 @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_add: {
                    data.enforceInterface(descriptor);
                    com.demo.aidlservice.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.demo.aidlservice.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    java.util.List<com.demo.aidlservice.Person> _result = this.add(_arg0);
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

通过上面的分析,读者应该已经了解了Binder的工作机制,但是有两点还是需要额外 说明一下:首先,当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回 数据,所以如果一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求;其 次,由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都 应该采用同步的方式去实现,因为它已经运行在一个线程中了。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值