2019独角兽企业重金招聘Python工程师标准>>>

一、关于Parcel

1.支持的数据类型

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

 

2.Parcel序列化与反序列化

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

此工具主要用于序列化过程中的“转换逻辑”,也就是序列化与反序列化。简单来说,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数据是如何在被写入的。

 

3.简单示例

还是按照惯例,给一个例子:

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 CREATOR = new Creator() {    @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); }}

使用方式

    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反序列化通过神奇魔法创建对象的黑科技强多了吧?

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> 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 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通信时再补充。

 

二、反向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);    }}  @Overridepublic int describeContents() {        return 0;}@Overridepublic void writeToParcel(Parcel out, int flags) {        out.writeStrongBinder(new MyBinder());}/**** 反序列化时获取Binder**/public static final Parcelable.Creator CREATOR            = new Parcelable.Creator() {        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,因此就实现了双向通信。

 

三、实现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;    }}

 

注册

  

参考

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

 

 

转载于:https://my.oschina.net/ososchina/blog/1615505

更多相关文章

  1. 一句话锁定MySQL数据占用元凶
  2. Animations使用(一)
  3. Android(安卓)如何用HttpClient 以Get方式获取数据并添加http头
  4. android中的数据存储 收藏
  5. Android(安卓)Framework下StageFright框架流程解读
  6. Android(安卓)Fragment与Fragment之间数据获取
  7. Android(安卓)DataBinding+ViewModel+LiveData小Demo
  8. Android中的网络编程系列(一):URLConnection
  9. Android(安卓)IPC 进程间通信机制之 Messenger

随机推荐

  1. Drawable分类
  2. Activity生命周期详解
  3. 简述Android消息机制及解惑
  4. android SQLite 鎬荤粨
  5. Android基础开发之手势识别
  6. Android实现视频播放的3种实现方式
  7. Android应用项目绑定appcom_v7打包时,出现
  8. Android(安卓)秒级编译方案-Freeline安装
  9. Android(安卓)studio 添加assets文件夹
  10. Android(安卓)应用程序之间数据共享—Con