Parcelable是Android用來進行序列化的接口,主要支持的類型有常見的數據類型(除short),還有List、Map、Set、Parcelable數據,此外還支持android.util.Size、android.util.SizeF、SparseArray,SparseBooleanArray、ArrayMap、FileDescriptor和IBinder。java
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
仍是按照慣例,給一個例子: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
下面咱們看一眼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通訊時再補充。
對於常見的使用Binder的方式具體有ContentProvider、Service、Intent、Bluetooth和Messenger,通常Request/Response都是C/S或者B/S架構,固然,咱們這裏所要說的並不違反這一原則,由於在咱們的C/S結構中,服務的角色沒法反向,可是的通訊工具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; } };
所謂反向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,所以就實現了雙向通訊。
咱們能夠經過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);
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; } }
這裏咱們不在深刻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