客户端是如何通过Binder与服务端通信的

代码演示

我们模仿AIDL工具帮我们生成的类以便加入日志:

// IPersonManager.aidl
package com.example.chricservice;

// Declare any non-default types here with import statements
import com.example.chricservice.Person;

interface IPersonManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
      void addPerson(in Person person);

      List<Person> getPersonList();

      void setData(in byte[] data);
}

AIDL生成代码结构如下图:

我们只关注核心代码 如下:

package com.example.chricbinder;

public interface IPersonManager extends IInterface {
    void addPerson(Person person) throws RemoteException;

    List<Person> getPersonList() throws RemoteException;

    void setData(byte[] data) throws RemoteException;
    
        public static abstract class Stub extends Binder implements IPersonManager {
        static final String DESCRIPTOR = "com.example.chricbinder.IPersonManager";
        static final int TRANSACTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;
        static final int TRANSACTION_getPersonList = IBinder.FIRST_CALL_TRANSACTION + 1;
        static final int TRANSACTION_setData = IBinder.FIRST_CALL_TRANSACTION + 2;

        public static IPersonManager asInterface(IBinder binder) {
            if ((binder == null)) {
                return null;
            }
            //尝试检索一个接口的本地实现用于此Binder对象。如果返回null,则需要实例化一个代理类来调用transact()方法。
            IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
            if ((iin != null) && (iin instanceof IPersonManager)) {
                Log.e("BinderChric", " 【CLIENT】(IPersonManager) iin " );
                return (IPersonManager) iin;
            }
            Log.e("BinderChric", "【CLIENT】 new Proxy(binder) " + binder);
            return new Proxy(binder);
        }

        @Override
        public IBinder asBinder() {
            return this;
        }

        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            String DESCRIPTOR = "com.example.chricbinder.IPersonManager";
            switch (code) {
                case INTERFACE_TRANSACTION:
                    reply.writeString(DESCRIPTOR);
                    return true;

                case TRANSACTION_addPerson:
                    Log.e("BinderChric", "【SERVER】==Stub,onTransact addPerson: " + Thread.currentThread());
                    data.enforceInterface(DESCRIPTOR);
                    Person arg0 = null;
                    if ((0 != data.readInt())) {
                        arg0 = Person.CREATOR.createFromParcel(data);
                    }
                    this.addPerson(arg0);
                    reply.writeNoException();
                    return true;

                case TRANSACTION_getPersonList:
                    Log.e("BinderChric", "【SERVER】==Stub,onTransact PersonList: " + Thread.currentThread());
                    data.enforceInterface(DESCRIPTOR);
                    List<Person> result = this.getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    return true;

                case TRANSACTION_setData:
                    data.enforceInterface(DESCRIPTOR);
                    byte[] _arg0;
                    _arg0 = data.createByteArray();
                    this.setData(_arg0);
                    reply.writeNoException();
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements IPersonManager {

            private IBinder mRemote;

            public Proxy(IBinder remote) {
                mRemote = remote;
            }

            @Override
            public void addPerson(Person person) throws RemoteException {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                try {
                    data.writeInterfaceToken(DESCRIPTOR);
                    if ((person != null)) {
                        data.writeInt(1);
                        person.writeToParcel(data, 0);
                    } else {
                        data.writeInt(0);
                    }
                    Log.e("BinderChric", "【CLIENT】==Proxy transact addPerson: " + Thread.currentThread());
                    mRemote.transact(Stub.TRANSACTION_addPerson, data, reply, 0);
                    reply.readException();
                } finally {
                    reply.recycle();
                    data.recycle();
                }
            }

            @Override
            public List<Person> getPersonList() throws RemoteException {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                List<Person> result;
                try {
                    data.writeInterfaceToken(DESCRIPTOR);
                    Log.e("BinderChric", "【CLIENT】==Proxy transact getPersonList: " + Thread.currentThread());
                    mRemote.transact(Stub.TRANSACTION_getPersonList, data, reply, 0);
                    reply.readException();
                    result = reply.createTypedArrayList(Person.CREATOR);
                } finally {
                    reply.recycle();
                    data.recycle();
                }
                return result;
            }

            @Override
            public void setData(byte[] data) throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeByteArray(data);
                    // 同步:0  异步:IBinder.FLAG_ONEWAY
                    mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public IBinder asBinder() {
                return mRemote;
            }
        }
    }
}

客户端Activity代码:

package com.example.chricbinder.client;

public class ClientActivity extends AppCompatActivity {
    Button button1;
    Button button2;
    Button button3;
    Button button4;
    IPersonManager pmsProxy;
    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.w("BinderChric", "【CLIENT】== onServiceConnected");
            // proxy
            pmsProxy = IPersonManager.Stub.asInterface(service);
        }

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

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);
        button3 = findViewById(R.id.button3);
        button4 = findViewById(R.id.button4);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ClientActivity.this, RemoteService.class);
                intent.setAction("com.chric.binder");
                boolean success = bindService(intent, serviceConnection, BIND_AUTO_CREATE);
                Log.w("BinderChric", "【CLIENT】== bindService success =" + success);
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Person person = new Person("BinderChric", 28);
                    Log.w("BinderChric", "【CLIENT】== addPerson:" + person);
                    pmsProxy.addPerson(person);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    List<Person> personList = pmsProxy.getPersonList();
                    Log.w("BinderChric", "【CLIENT】== personList:" + personList.toString());
                } catch (RemoteException e) {

                }
            }
        });

        button4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(serviceConnection);
            }
        });

    }
}

服务端代码:

package com.example.chricbinder.server;

public class RemoteService extends Service {
    // 这里模仿ActivityManagerService extends IActivityManager.Stub
    IBinder pms = new PersonManagerService();
    // 这里使用匿名内部类实现Stub
//    private ArrayList<Person> persons = new ArrayList<>();
//
//    IBinder pms = new IPersonManager.Stub() {
//        @Override
//        public void addPerson(Person person) throws RemoteException {
//            persons.add(person);
//        }
//
//        @Override
//        public List<Person> getPersonList() throws RemoteException {
//            return persons;
//        }
//
//        @Override
//        public void setData(byte[] data) throws RemoteException {
//
//        }
//    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.w("BinderChric", "【SERVER】== RemoteService onBind");
        return pms;
    }

    @Override
    public void onCreate() {
        Log.w("BinderChric", "【SERVER】== RemoteService onCreate ");
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    public static class PersonManagerService extends IPersonManager.Stub{
        private ArrayList<Person> persons = new ArrayList<>();

        @Override
        public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override
        public List<Person> getPersonList() throws RemoteException {
            return persons;
        }

        @Override
        public void setData(byte[] data) throws RemoteException {

        }
    }
}

运行日志如下(这里客户端和服务端在一个进程,当然大家可以把服务端运行在另外一个进程,这样CS两端都需要写IPersonManager接口协议):

 W/BinderChric: 【CLIENT】== bindService success =true
 W/BinderChric: 【SERVER】== RemoteService onCreate 
 W/BinderChric: 【SERVER】== RemoteService onBind
 W/BinderChric: 【CLIENT】== onServiceConnected
 E/BinderChric: 【CLIENT】 new Proxy(binder) com.example.chricbinder.server.RemoteService$PersonManagerService@a27f4bf
 W/BinderChric: 【CLIENT】== addPerson:Person{name='BinderChric', age=28}
 E/BinderChric: 【CLIENT】==Proxy transact addPerson: Thread[main,5,main]
 E/BinderChric: 【SERVER】==Stub, onTransact addPerson: Thread[main,5,main]
 E/BinderChric: 【CLIENT】==Proxy transact getPersonList: Thread[main,5,main]
 E/BinderChric: 【SERVER】==Stub, onTransact getPersonList: Thread[main,5,main]
 W/BinderChric: 【CLIENT】== personList:[Person{name='BinderChric', age=28}]

总结

binder 基于 AIDL 的通信流程图如下:
Client 端: Client Activity
Server 端: Remote Service
Service 的对外 aidl 接口如上面代码所示,
Client Activity已经通过BindService拿到了Remote Service的IBinder 对象
AIDL通信的基本步骤如下:
1. Client通过ServiceConnection获取到ServerBinder,并且封装成一个Proxy
2. 通过Proxy来同步调用IPC方法(addPerson,Parcel data和Parcel reply),同时通过
person.writeToParcel(data, 0)将参数传给Binder,最终触发Binder的 transact方法。
3. Bindertransact方法最终会触发到ServerStubonTransact方法。
4. ServerStub的onTransact方法中,会先从Parcel data中解析中参数,然后将参数带入真正的方法中执行,然后将 结果写入Parcel reply后传回(例如调用getPersonList方法中result = reply.createTypedArrayList(Person.CREATOR))。
5. 请注意: ClientIPC方法中,执行Bindertransact时,是同步阻塞等待的, 一直到Server逻辑执行结束后才会继 续执行。当然,如果IPC方法是oneWay的方式,那么就是非阻塞的等待。
6. Server返回结果后, ClientParcel中取出返回值,于是实现了一次IPC调用。
7.aidl是一个使用 binder的标准方案,该方案的代码同样的可以通过用户自己编写的方式完成;
aidl 文件build后会生成一个java文件,这个java文件的意义在于将核心的binder驱动封装成为java层可以直接调用的 代码,同时也处理了将java层的数据格式转换为Parcel格式数据进行跨进程传递的一个功能。

后记

1.客户端是如何通过bindservice获取服务端的BinderProxy对象的?

2.Binder驱动及周边是什么?BpBinder、BBinder是什么?

后面会继续更新。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值