Android 自定义Binder方法

266 阅读3分钟

第一种方式

1. 服务端:


public class MyService extends Service {
    private static final String TAG = "MyService";
    private static final int CODE_DO = 1;

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return myBinder;
    }

    Binder myBinder = new Binder(){
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

            if (code == CODE_DO) {
                int a = data.readInt();
                int result = plusOne(a);
                reply.writeInt(a);
                Log.i(TAG, "result value is" + result);
                return true;
            }

            return super.onTransact(code, data, reply, flags);
        }
    };
    
    public int plusOne(int a) {
        return a++;
    }
}


步骤说明:

  • 自定义了一个myBinder,并重写了onTransact()方法
  • 在onTransact()方法中通过parcel data读取客户端传过来的值,并写入到另一个parcel reply中

2.客户端:


ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        mBinder = iBinder;
        transactValue(2);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }
};

private void transactValue(int i) {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInt(i);
    try {
        //调用远程服务
        mBinder.transact(CODE_DO,data,reply,0);
        //获取远程服务返回的值
        int result = reply.readInt();
        Log.i(TAG, " result " + result);
    } catch (RemoteException e) {
        throw new RuntimeException(e);
    }
}

步骤说明:

  • 绑定服务后通过serviceConnection获取服务端的IBinder对象 mBinder
  • 构造一个Parcel data 然后写入数据到data中,构造一个Parcel reply 读取数据
  • 通过transact调用远程服务
  • reply 通过readInt()读取远程服务返回的值

3. 小结

  • 需求:客户端的目的是向服务端传值,服务端的目的是把客户端传过来的值进行处理然后返回给客户端
  • 缺点:这段代码对客户端暴露了Binder的具体细节transact,对服务端也暴露了Binder的具体细节onTransact
  • 优化方向:将服务端需要实现的方法放到一个接口中,自定义一个Binder来继承这个接口,通过代理模式来隐藏具体的细节,见第二种方式

第二种方式

1. 把plusOne方法放到一个接口中

public interface IPlusOne extends android.os.IInterface {
    //binder 描述符
    static final String DESCRIPTOR = "com.example.myapplication.IPLUSOne";
    static final int TRANSACTION_plusOne = IBinder.FIRST_CALL_TRANSACTION;
    public int plusOne (int a) throws RemoteException;
}

2. 自定义一个Binder类

屏幕截图 2024-04-06 155622.png

package com.example.myapplication;

import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;

public class PlusOneImpl extends android.os.Binder implements IPlusOne {
    @Override
    public int plusOne(int a) throws RemoteException {
        return 0;
    }

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

    public PlusOneImpl() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static IPlusOne asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if ((iin != null) && (iin instanceof IPlusOne)) {
            return (IPlusOne) iin;
        }
        return new PlusOneImpl.Proxy(obj);
    }
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:{
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_plusOne: {
                data.enforceInterface(DESCRIPTOR);
                int args = data.readInt();
                int result =  this.plusOne(args);
                reply.writeInt(result);
                return  true;
            }
        }
        return super.onTransact(code,data,reply,flags);
    }


    private static class Proxy implements IPlusOne {

        private  IBinder mRemote;
        Proxy(IBinder remote) {
            mRemote = remote;
        }
        @Override
        public int plusOne(int a) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInt(a);
                mRemote.transact(TRANSACTION_plusOne,data,reply,0);
            } finally {
                reply.recycle();
                data.recycle();
            }
            return 0;
        }

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

步骤说明:

  • 使PlusOneImpl继承Binder类,从而可以重写Binder的onTransact()方法,接收客户端传来的数据
  • PlusOneImpl实现IPlusOne的接口,从而可以在服务端实现plusOne()方法
  • PlusOneImpl 通过asInterface(IBinder)来构建一个Proxy类给客户端使用,IBinder即PlusOneImpl自身
  • Proxy类实现了PlusOneImpl接口,从而客户端可以调用到Proxy端的plusOne()方法
  • 这个Proxy中包含一个IBinder对象,在plusOne()方法中Proxy通过IBinder来执行transact方法进行跨进程访问

3. 服务端:


public class MyService extends Service {
    private static final String TAG = "MyService";

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return myBinder;
    }

    private final PlusOneImpl myBinder = new PlusOneImpl() {

        @Override
        public int plusOne(int a) throws RemoteException {
            return a++;
        }
    };
}

4. 客户端:

ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        IPlusOne mPlusOne = mPlusOneImpl.asInterface(iBinder);
        try {
            mPlusOne.plusOne(3);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }
};

小结:

  • 将服务端所需要的实现的方法,放到一个接口中,然后让自定义的Binder去实现这个接口,最终在服务端初始化自定义的Binder并重写这个接口以实现具体的功能,这是通过依赖倒置让Binder去依赖Service,从而实现Service对Binder的把控

  • 跨进程通信过程中通过代理模式向客户端隐藏具体细节