Android Parcel实现反向Binder通讯

1、关于Parcel

1.支持的数据类型

Parcelable是Android用来进行序列化的接口,主要支持的类型有常见的数据类型(除short),还有List、Map、Set、Parcelable数据,此外还支持android.util.Size、android.util.SizeF、SparseArray,SparseBooleanArray、ArrayMap、FileDescriptor和IBinder。java

 

2.Parcel序列化与反序列化

android.os.Parcel _dataParcel = android.os.Parcel.obtain();android

此工具主要用于序列化过程当中的“转换逻辑”,也就是序列化与反序列化。简单来讲,Parcel提供了一套机制,能够将序列化以后的数据写入到一个共享内存中,其余进程经过Parcel能够从这块共享内存中读出字节流,并反序列化成对象,下图是这个过程的模型。缓存

Parcel在Java层和C++层都有定义,Parcel中对于不一样类型的数据处理是不同的,它有两个成员:服务器

uint8_t* mData; //用来存储序列化流数据,能够把它理解成共享内存
size_t* mObjects;  //用来存储IBinder和FileDescriptor

为何要区别对待呢?咱们能够暂时这么理解,对于IBinder来讲,它存在的意义就是要现实跨进程调用,因此我就是须要在Parcel中传递一个真实的引用,这个引用可以操做到发起进程的对象。实际上在Parcel的C++层也有一个单独的结构来描述将要写入的Binder对象:flat_binder_object。而对于文件描述符来讲,原本就是kernel层的东西,因此在不一样的进程中它们能够表示同一个对象,因此也无需严格的序列化。架构

咱们这里暂不讨论讨论IBinder和FileDescriptor序列化的问题,咱们只关心基本类型数据和Parcelable数据是如何在被写入的。app

 

3.简单示例

仍是按照惯例,给一个例子:dom

class Pojo implements Parcelable {

 protected String desc;

 private Pojo(Parcel in) {
    desc = in.readString();
 }

 public Pojo(String desc) {
    this.desc = desc;
 }

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

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

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

 @Override
 public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(desc);
 }
}

使用方式socket

Parcel parcel = Parcel.obtain();
    Pojo pojo = new Pojo("TEST");

    //写入Parcel
    parcel.writeParcelable(pojo,0);
    //Parcel读写共用一个位置计数,这里必定要重置一下当前的位置
    parcel.setDataPosition(0);
    //读取Parcel
    Pojo pojo1 = parcel.readParcelable(Pojo.class.getClassLoader());
    Log.d(TAG,pojo1.desc);

仔细看看这个设计,你会不自觉地想这个东西怎么这么眼熟...是的,你没有看错,是否是和[Java序列化的代理模式][1]很像?CREATOR的createFromParcel经过new的方式从流中读取一个对象,这比Java反序列化经过神奇魔法建立对象的黑科技强多了吧?ide

4.Parcelable读写

下面咱们看一眼Parcel读写的代码,注意,我这里只看Java层,你能够这么想象一下,好比我调用Java层的Parcel.writeInt(),会调用到C++层Parcel的writeInt(),而后在其中直接进行内存的复制操做memcpy,将数据复制到共享内存中。工具

Parcel.writeParcelable

public final void writeParcelable(Parcelable p, int parcelableFlags) {
    //先向流中写入p的ClassName
    writeParcelableCreator(p);
    //而后直接调用p的writeToParcel,这个方法也就是咱们本身重写的
    p.writeToParcel(this, parcelableFlags);
}

这里须要注意的是,每个Parcelable对象在写入流以前,都会在前面首先写入这个对象的ClassName,主要是方便后面读的时候,可以知道是哪一个类,感受这个地方仍是作的比较粗糙,在Serializable中对应一个序列化类的信息刻画比这简单的一个类名要靠谱得多,因此官方文档上才会说,若是你想进行持久化存储,那么Parcelable不是你的菜,道理很简单,这里不会有任何版本的概念,只要你的类名不改,旧版本的数据就能够被新版本的class进行反序列化,然而class里面的域可能已经彻底不同了。

Parcel.readParcelable

首先会调用到readParcelableCreator,经过反射读取咱们类中定义的CREATOR:

public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
 //首先把类名读取出来
    String name = readString();

    Parcelable.Creator<?> creator;
    //mCreators作了一下缓存,若是以前某个classloader把一个parcelable的Creator获取过
    //那么就不须要经过反射去查找了
    
    synchronized (mCreators) {
        HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
        if (map == null) {
            map = new HashMap<>();
            mCreators.put(loader, map);
        }
        creator = map.get(name);
        if (creator == null) {
            try {
                // If loader == null, explicitly emulate Class.forName(String) "caller
                // classloader" behavior.
                ClassLoader parcelableClassLoader =
                        (loader == null ? getClass().getClassLoader() : loader);
                //加载咱们本身实现Parcelable接口的类
                Class<?> parcelableClass = Class.forName(name, false,
                        parcelableClassLoader);
                
                Field f = parcelableClass.getField("CREATOR");
                Class<?> creatorType = f.getType();
                creator = (Parcelable.Creator<?>) f.get(null);
            }
            catch (Exception e) {
                    //catch exception
            }
            if (creator == null) {
                throw new BadParcelableException("Parcelable protocol requires a "
                        + "non-null Parcelable.Creator object called "
                        + "CREATOR on class " + name);
            }

            map.put(name, creator);
        }
    }

    return creator;
}

而后直接调用CREATOR.createFromParcel(parcel)

public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
    Parcelable.Creator<?> creator = readParcelableCreator(loader);
    if (creator == null) {
        return null;
    }
    if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
      Parcelable.ClassLoaderCreator<?> classLoaderCreator =
          (Parcelable.ClassLoaderCreator<?>) creator;
      return (T) classLoaderCreator.createFromParcel(this, loader);
    }
    return (T) creator.createFromParcel(this);
}

好了,Parcel介绍到这里。注意,在上文中,为了将内容更加简明清晰,我把Parcel中内存共享部分简单带过了一下,至于更加严谨的关于共享内存部分的细节,之后我会在写IPC通讯时再补充。

 

2、反向Binder通讯

对于常见的使用Binder的方式具体有ContentProvider、Service、Intent、Bluetooth和Messenger,通常Request/Response都是C/S或者B/S架构,固然,咱们这里所要说的并不违反这一原则,由于在咱们的C/S结构中,服务的角色没法反向,可是的通讯工具Binder能够反向,在服务器开发中反向代理也是一种经常使用的方式。

1.正向Binder通讯

不管是对于Service仍是ContentProvider,通常的话,Binder的内部实现都是从Service端构造的。常见的就是咱们经过绑定服务的方式访问Service。

服务端Binder

private static final java.lang.String DESCRIPTOR = "org.ninetripods.mq.multiprocess_sever.IAidlCallBack";
private static final int KEY_FLAG = 0x110;
 
private class MyBinder extends Binder {
    /**
     * @param code 惟一标识,客户端传递标识执行服务端代码
     * @param data 客户端传递过来的参数
     * @param reply 服务器返回回去的值
     * @param flags 是否有返回值 0:有 1:没有
     * @return
     * @throws RemoteException 异常
     */
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
      switch (code) {
        case KEY_FLAG:
          //标识服务器名称
          data.enforceInterface(DESCRIPTOR);
          Apple apple = new Apple("红星苹果", 15f, getString(R.string.response_binder_info));
          reply.writeNoException();
          reply.writeInt(1);
          apple.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          return true;
      }
 
      return super.onTransact(code, data, reply, flags);
    }
  }
 
  @Override
  public IBinder onBind(Intent intent) {
    return new MyBinder();
  }

客户端Binder

private ServiceConnection binderConnection = new ServiceConnection() {  //绑定服务监听器
    @Override
    public void onServiceConnected(ComponentName name, IBinder binder) {
      isBound = true;
      mService = binder; //获取到Binder
      if (mService != null) {
        //声明两个Parcel类型数据(_data和_reply) 一个用于传输数据 一个用于接收数据
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        Apple apple;
        try {
          //与服务器端的enforceInterface(DESCRIPTOR)对应
          _data.writeInterfaceToken(DESCRIPTOR);
          //调用服务端的transact()传输数据
          mService.transact(KEY_FLAG, _data, _reply, 0);
          _reply.readException();
          if (0 != _reply.readInt()) {
            //接收服务端响应数据
            apple = Apple.CREATOR.createFromParcel(_reply);
          } else {
            apple = null;
          }
          showMessage(apple != null ? ("\n" + apple.getNoticeInfo() + "\n名称:"
              + apple.getName() + "\n价格:" + apple.getPrice() + " 元") : "未得到服务器信息", R.color.red_f);
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
          _data.recycle();
          _reply.recycle();
        }
      }
    }
 
    @Override
    public void onServiceDisconnected(ComponentName name) {
      isBound = false;
      mService = null;
    }
  };

 

2.反向Binder通讯

所谓反向Binder,其实很简单,只须要咱们将Binder中的通讯部分反向一下,可是咱们要解决以下三个问题。

①保证C/S和B/S模型中的角色不变,即Service和Activity的角色地位不变,也就是Service给Activity发送请求。

②Activity发送Binder,Service获取到Request(这里能够认为是Client端的Binder)。

③Service发送Response到Binder,确保Activity获取到(Binder的返回值)。

 

对于问题①而言,咱们能够将咱们的服务定义到Service组件中。

下面列子中,暴露getRandomNumber为Service接口。

public class MyService extends Service{

    public RequestBinder  binder = null;

    private final Random generator = new Random();

    @Override
    public void onCreate() {
        Log.i("Kathy","TestTwoService - onCreate - Thread = " + Thread.currentThread().getName());
        super.onCreate();
  
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("Kathy", "TestTwoService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName());
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i("Kathy", "TestTwoService - onDestroy - Thread = " + Thread.currentThread().getName());
        super.onDestroy();
    }

    //getRandomNumber是Service暴露出去供client调用的公共方法
    public int getRandomNumber() {
        return generator.nextInt();
    }
}

 

对于问题②而言,让Service可以监听Activity请求,那么Activity就得主动和Service通讯,常见通讯方式有startService、bindService、广播、Localsocket等。

首先咱们这个Service中不存在Binder,咱们没法使用bindService、至于广播和Localsocket那么咱们不叫反向通讯了,所以剩下的只能考虑startService。

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("Kathy", "TestTwoService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName());

         RequestBinder binder = intent.getParcelableExtra("RequestBinder");

        this.binder = binder ;
  

        return START_NOT_STICKY;
    }

 

对于问题③,startService传递参数须要经过Intent,而Intent不能直接发送Binder,接下来如何处理?答案是——Parcelable。

实现Client端的Parcelable

public final class RequestBinder implements Parcelable
{
   
private static final java.lang.String DESCRIPTOR = "org.mq.multiprocess.IAidlCallBack";

private static final int KEY_FLAG = 0x110;


private IBinder binder;

/**
** 用于客户端调用
**/
public RequestBinder()
{
   this(new MyBinder());
}
/**
 *
*用于反向端(service)
*/
public RequestBinder(IBinder binder)
{
   this.binder = binder;
}
/**
**用于服务端发送消息
**/
 public final boolean transact(int code, Parcel data, Parcel reply,int flags) throws RemoteExceptio
        
        return this.binder.transact(code,data,reply,flags);
 }

 
private static class MyBinder extends Binder {
    /**
     * @param code 惟一标识,客户端传递标识执行服务端代码
     * @param data 客户端传递过来的参数
     * @param reply 服务器返回回去的值
     * @param flags 是否有返回值 0:有 1:没有
     * @return
     * @throws RemoteException 异常
     */
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
      switch (code) {
        case KEY_FLAG:
          //标识服务器名称
          data.enforceInterface(DESCRIPTOR);
          Apple apple = new Apple("红星苹果", 15f, getString(R.string.response_binder_info));
          reply.writeNoException();
          reply.writeInt(1);
          apple.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          return true;
      }
 
      return super.onTransact(code, data, reply, flags);
    }
}
 
 

@Override
public int describeContents() {
        return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
        out.writeStrongBinder(new MyBinder());
}
/**
** 反序列化时获取Binder
**/
public static final Parcelable.Creator<Messenger> CREATOR
            = new Parcelable.Creator<Messenger>() {
        public RequestBinder createFromParcel(Parcel in) {
            IBinder target = in.readStrongBinder(); //读取binder
            return new RequestBinder(target);
        }

        public Messenger[] newArray(int size) {
            return new RequestBinder[size];
        }
    };
     

}

 

经过以上方式,咱们能够在Activity中发送Binder,也能够在Service获取Binder,所以就实现了双向通讯。

 

3、实现AIDL的反向通讯

1.实现条件

咱们能够经过Binder实现反向通讯,那么AIDL是否也能够呢?AIDL底层也是Binder来实现的,所以,彻底没有问题,咱们能够参考AIDL实现的例子,Stub自己继承自Binder,所以彻底能够实现。

interface UserInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    String getUserAge(in String name);
}

编译

public interface UserInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.siberiadante.multiscrolldemo.UserInterface
{
private static final java.lang.String DESCRIPTOR = "com.siberiadante.multiscrolldemo.UserInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.siberiadante.multiscrolldemo.UserInterface interface,
 * generating a proxy if needed.
 */
public static com.siberiadante.multiscrolldemo.UserInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.siberiadante.multiscrolldemo.UserInterface))) {
return ((com.siberiadante.multiscrolldemo.UserInterface)iin);
}
return new com.siberiadante.multiscrolldemo.UserInterface.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_getUserAge:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.getUserAge(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.siberiadante.multiscrolldemo.UserInterface
{
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.lang.String getUserAge(java.lang.String name) 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);
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_getUserAge, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getUserAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String getUserAge(java.lang.String name) throws android.os.RemoteException;
}

 

咱们须要实现Stub

public class UserBinder extends UserInterface.Stub {


    private final Handler handler;

    public UserBinder(Handler mHandler) {
        this.handler = mHandler;
    }

    @Override
    public String getUserAge(String name) throws RemoteException {
       String res = "0";
        if("zhangsan".equals(name)){
            res =  "20";
        }else  if("lisi".equals(name)){
            res = "21";
        }
        Message obtain = Message.obtain(handler, 200, name);
        obtain.sendToTarget();
        return res;
    }


}

而后用下列方式传递

private final Handler mHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what==100) {
                Bundle bundle = (Bundle) msg.obj;
                bundle.setClassLoader(Teacher.class.getClassLoader());
                Teacher teacher = bundle.getParcelable("teacher");
                Toast.makeText(BannerActivity.this, "执行成功:" + teacher, Toast.LENGTH_LONG).show();
            }else if(msg.what==200){
                Toast.makeText(BannerActivity.this, "执行成功:" + msg.obj, Toast.LENGTH_LONG).show();
            }
        }
    };



Intent sIntent  = new Intent(this, RemoteService.class);
sIntent.putExtra("messenger",new Messenger(mHandler));
if(Build.VERSION.SDK_INT>=18) {
            Bundle b = new Bundle();
            b.setClassLoader(UserBinder.class.getClassLoader());
            b.putBinder("user", new UserBinder(mHandler));
            sIntent.putExtra("binder",b);
 }
startService(sIntent);

Service端

public class RemoteService extends Service {
    public RemoteService() {
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        sendMessage(intent);
        return super.onStartCommand(intent, flags, startId);
    }

    private void sendMessage(Intent intent)  {
        try {
           
            if(Build.VERSION.SDK_INT>=18) {
                Bundle data = intent.getBundleExtra("binder");
                data.setClassLoader(UserBinder.class.getClassLoader());
                IBinder user = data.getBinder("user");
                Log.e("binder","age="+user.getClass());
                UserInterface userInfo= UserInterface.Stub.asInterface(user);

                Log.e("binder","---->"+userInfo.getUserAge("zhangsan"));
            }


        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {

        return null;
    }



}

2.Messenger示例

这里咱们不在深刻Parcelable讨论,在Android平台中,Messenger底层基于Handler的AIDL,你们能够参考Messenger的反向通讯。

Client端

private final Handler mHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what==100) {
                Bundle bundle = (Bundle) msg.obj;
                bundle.setClassLoader(Teacher.class.getClassLoader()); //必须设置,不然没法反序列化
                Teacher teacher = bundle.getParcelable("teacher");
                Toast.makeText(BannerActivity.this, "执行成功:" + teacher, Toast.LENGTH_LONG).show();
            }else if(msg.what==200){
                Toast.makeText(BannerActivity.this, "执行成功:" + msg.obj, Toast.LENGTH_LONG).show();
            }
        }
    };


 Intent sIntent  = new Intent(this, RemoteService.class);
  sIntent.putExtra("messenger",new Messenger(mHandler));
startService(sIntent);

Service端

 

public class RemoteService extends Service {
    public RemoteService() {
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        sendMessage(intent);
        return super.onStartCommand(intent, flags, startId);
    }

    private void sendMessage(Intent intent)  {
        try {
            Messenger messenger = intent.getParcelableExtra("messenger");
            Message message = Message.obtain();
            Bundle b = new Bundle();
            b.putString("message","来自remote进程的消息");
            b.setClassLoader(Teacher.class.getClassLoader());
//Android 最新版本不支持Message.obj直接传递Parcelable对象(会触发类不存在异常),所以须要将对象放到Bundle中。
            b.putParcelable("teacher",new Teacher("张三","男"));
            message.obj = b;
            messenger.send(message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {

        return null;
    }



}

 

注册

<service
            android:name=".RemoteService"
            android:enabled="true"
            android:exported="false"
            android:process=":remote"
            ></service>

参考

Android序列化彻底解析(一)-Java Serializable
Android序列化彻底解析(二)-Parcelable
Android序列化彻底解析(三)-拨乱反正,堪比窦娥的Serializable

相关文章
相关标签/搜索