關於Binder,我就不解釋的太多了,網上一搜資料一堆,但是估計還是很多人理解的有困難。今天就教你如何從 app層面來理解好Binder。
其實就從我們普通app開發者的角度來看,僅僅對於android應用層的話,Binder就是客戶端和服務端進行通訊的媒介。
AIDL就是我們理解Binder 最好的事例。
我們都知道 我們寫好aidl 檔案以後,開發工具 會自動幫我們生成好程式碼。實際上 我們最終apk裡面 是隻有這些程式碼的,我們寫的aidl檔案
是不會被打包進去的,也就是說aidl檔案 實際上 就是我們用來 生成 實際binder程式碼用的。所以 我們只要能夠分析好,ide自動幫我們生成的
程式碼,就可以自己手寫binder,從而在app層面上真正理解binder的用法和含義 以及原理。
首先我先來定義一個實體類:Person.java
1 package com.example.administrator.writebindercodeexample; 2 3 import android.os.Parcel; 4 import android.os.Parcelable; 5 6 /** 7 * Created by Administrator on 2016/1/27. 8 */ 9 public class Person implements Parcelable { 10 11 private String name; 12 13 public void setName(String name) { 14 this.name = name; 15 } 16 17 public void setGender(int gender) { 18 this.gender = gender; 19 } 20 21 public int getGender() { 22 return gender; 23 } 24 25 public String getName() { 26 return name; 27 } 28 29 private int gender; 30 31 @Override 32 public int describeContents() { 33 return 0; 34 } 35 36 @Override 37 public void writeToParcel(Parcel dest, int flags) { 38 dest.writeString(this.name); 39 dest.writeInt(this.gender); 40 } 41 42 public Person() { 43 } 44 45 protected Person(Parcel in) { 46 this.name = in.readString(); 47 this.gender = in.readInt(); 48 } 49 50 public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() { 51 public Person createFromParcel(Parcel source) { 52 return new Person(source); 53 } 54 55 public Person[] newArray(int size) { 56 return new Person[size]; 57 } 58 }; 59 }
注意看 我們這個person 類 是實現了android自帶的序列化介面的,所以 如果你要在aidl裡使用這個類,那你必須要額外在aidl裡生命下 這個類。
1 // Person.aidl.aidl 2 package com.example.administrator.writebindercodeexample; 3 4 // Declare any non-default types here with import statements 5 parcelable Person;
1 // IPersonManager.aidl 2 package com.example.administrator.writebindercodeexample; 3 4 // Declare any non-default types here with import statements 5 import com.example.administrator.writebindercodeexample.Person; 6 interface IPersonManager { 7 List<Person> getPersonList(); 8 //關於這個引數in 其實你不加也是可以編譯通過的,這裡我就先加一下 具體引數的意義 以後會說 9 void addPerson(in Person person); 10 }
好,然後給你們看一下 檔案結構:
好 這裡就是一個典型的 應用aidl 技術的 一個例子,我們現在 讓studio 編譯這個project,然後看看生成的binder程式碼。 把這份binder程式碼 分析好了,我們以後就可以不借助ide 來自己手寫binder了。
我們來看看 生成的程式碼在哪裡:
最後我們來看一下 這個生成的程式碼 是啥樣的:
1 /* 2 * This file is auto-generated. DO NOT MODIFY. 3 * Original file: C:\\Users\\Administrator\\WriteBinderCodeExample\\app\\src\\main\\aidl\\com\\example\\administrator\\writebindercodeexample\\IPersonManager.aidl 4 */ 5 package com.example.administrator.writebindercodeexample; 6 public interface IPersonManager extends android.os.IInterface 7 { 8 /** Local-side IPC implementation stub class. */ 9 public static abstract class Stub extends android.os.Binder implements com.example.administrator.writebindercodeexample.IPersonManager 10 { 11 private static final java.lang.String DESCRIPTOR = "com.example.administrator.writebindercodeexample.IPersonManager"; 12 /** Construct the stub at attach it to the interface. */ 13 public Stub() 14 { 15 this.attachInterface(this, DESCRIPTOR); 16 } 17 /** 18 * Cast an IBinder object into an com.example.administrator.writebindercodeexample.IPersonManager interface, 19 * generating a proxy if needed. 20 */ 21 public static com.example.administrator.writebindercodeexample.IPersonManager asInterface(android.os.IBinder obj) 22 { 23 if ((obj==null)) { 24 return null; 25 } 26 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 27 if (((iin!=null)&&(iin instanceof com.example.administrator.writebindercodeexample.IPersonManager))) { 28 return ((com.example.administrator.writebindercodeexample.IPersonManager)iin); 29 } 30 return new com.example.administrator.writebindercodeexample.IPersonManager.Stub.Proxy(obj); 31 } 32 @Override public android.os.IBinder asBinder() 33 { 34 return this; 35 } 36 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException 37 { 38 switch (code) 39 { 40 case INTERFACE_TRANSACTION: 41 { 42 reply.writeString(DESCRIPTOR); 43 return true; 44 } 45 case TRANSACTION_getPersonList: 46 { 47 data.enforceInterface(DESCRIPTOR); 48 java.util.List<com.example.administrator.writebindercodeexample.Person> _result = this.getPersonList(); 49 reply.writeNoException(); 50 reply.writeTypedList(_result); 51 return true; 52 } 53 case TRANSACTION_addPerson: 54 { 55 data.enforceInterface(DESCRIPTOR); 56 com.example.administrator.writebindercodeexample.Person _arg0; 57 if ((0!=data.readInt())) { 58 _arg0 = com.example.administrator.writebindercodeexample.Person.CREATOR.createFromParcel(data); 59 } 60 else { 61 _arg0 = null; 62 } 63 this.addPerson(_arg0); 64 reply.writeNoException(); 65 return true; 66 } 67 } 68 return super.onTransact(code, data, reply, flags); 69 } 70 private static class Proxy implements com.example.administrator.writebindercodeexample.IPersonManager 71 { 72 private android.os.IBinder mRemote; 73 Proxy(android.os.IBinder remote) 74 { 75 mRemote = remote; 76 } 77 @Override public android.os.IBinder asBinder() 78 { 79 return mRemote; 80 } 81 public java.lang.String getInterfaceDescriptor() 82 { 83 return DESCRIPTOR; 84 } 85 @Override public java.util.List<com.example.administrator.writebindercodeexample.Person> getPersonList() throws android.os.RemoteException 86 { 87 android.os.Parcel _data = android.os.Parcel.obtain(); 88 android.os.Parcel _reply = android.os.Parcel.obtain(); 89 java.util.List<com.example.administrator.writebindercodeexample.Person> _result; 90 try { 91 _data.writeInterfaceToken(DESCRIPTOR); 92 mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0); 93 _reply.readException(); 94 _result = _reply.createTypedArrayList(com.example.administrator.writebindercodeexample.Person.CREATOR); 95 } 96 finally { 97 _reply.recycle(); 98 _data.recycle(); 99 } 100 return _result; 101 } 102 //關於這個引數in 其實你不加也是可以編譯通過的,這裡我就先加一下 具體引數的意義 以後會說 103 104 @Override public void addPerson(com.example.administrator.writebindercodeexample.Person person) throws android.os.RemoteException 105 { 106 android.os.Parcel _data = android.os.Parcel.obtain(); 107 android.os.Parcel _reply = android.os.Parcel.obtain(); 108 try { 109 _data.writeInterfaceToken(DESCRIPTOR); 110 if ((person!=null)) { 111 _data.writeInt(1); 112 person.writeToParcel(_data, 0); 113 } 114 else { 115 _data.writeInt(0); 116 } 117 mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); 118 _reply.readException(); 119 } 120 finally { 121 _reply.recycle(); 122 _data.recycle(); 123 } 124 } 125 } 126 static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 127 static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 128 } 129 public java.util.List<com.example.administrator.writebindercodeexample.Person> getPersonList() throws android.os.RemoteException; 130 //關於這個引數in 其實你不加也是可以編譯通過的,這裡我就先加一下 具體引數的意義 以後會說 131 132 public void addPerson(com.example.administrator.writebindercodeexample.Person person) throws android.os.RemoteException; 133 }
看上去呢,雜亂無章, 但其實也就是100多行,所以 我調整了一下 這個程式碼的順序 ,你們可以看的更清楚,同時也增加了註釋:
1 package com.example.administrator.aidlmessagetest; 2 3 //為了讓大家看的更清楚 我把生成的binder程式碼 給拷貝到另外一個工程下面了,並且用ide 給他format 4 //所以包名和我們一開始前面的程式碼都不一樣,大家理解意思就行。 5 6 7 //從前面幾行就能看出來 生成的程式碼是一個 interface ,只不過這個interface是 android.os.IInterface 的子類! 8 public interface IPersonManager extends android.os.IInterface { 9 10 //並且這個介面裡 有一個靜態的抽象類Stub(注意這個名字是固定的 永遠都是Stub 不會是其他) 11 //並且這個Stub是Binder的子類,並且實現了IPersonManager 這個介面 12 public static abstract class Stub extends android.os.Binder implements com.example.administrator.aidlmessagetest.IPersonManager { 13 //這個東西就是唯一的binder標示 可以看到就是IPersonManager的全路徑名 14 private static final java.lang.String DESCRIPTOR = "com.example.administrator.aidlmessagetest.IPersonManager"; 15 16 /** 17 * 這個就是Stub的構造方法,回顧一下 我們如果寫好aidl檔案以後 寫的service裡面 是怎麼寫的? 18 * 19 * private final IPersonManager.Stub mBinder = new IPersonManager.Stub() {} 20 * 我們都是這麼寫的 對吧~~所以想想我們的service裡面的程式碼 就能輔助理解 這裡的程式碼了 21 */ 22 public Stub() { 23 this.attachInterface(this, DESCRIPTOR); 24 } 25 26 27 //這個方法 其實就做了一件事,如果是同一個程式,那麼就返回Stub物件本身 28 //如果不是同一個程式,就返回Stub.Proxy這個代理物件了 29 public static com.example.administrator.aidlmessagetest.IPersonManager asInterface(android.os.IBinder obj) { 30 if ((obj == null)) { 31 return null; 32 } 33 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 34 //如果是同1個程式,也就是說程式內通訊的話 我們就返回括號內裡的物件 35 if (((iin != null) && (iin instanceof com.example.administrator.aidlmessagetest.IPersonManager))) { 36 return ((com.example.administrator.aidlmessagetest.IPersonManager) iin); 37 } 38 //如果不是同一程式,是2個程式之間相互通訊,那我們就得返回這個Stub.Proxy 看上去叫Stub 代理的物件了 39 return new com.example.administrator.aidlmessagetest.IPersonManager.Stub.Proxy(obj); 40 } 41 42 //返回當前物件 43 @Override 44 public android.os.IBinder asBinder() { 45 return this; 46 } 47 48 //只有在多程式通訊的時候 才會呼叫這個方法 ,同一個程式是不會呼叫的。 49 50 //首先 我們要明白 這個方法 一般情況下 都是返回true的,也只有返回true的時候才有意義,如果返回false了 就代表這個方法執行失敗, 51 //所以我們通常是用這個方法來做許可權認證的,其實也很好理解,既然是多程式通訊,那麼我們服務端的程式當然不希望誰都能過來呼叫 52 //所以許可權認證是必須的,關於許可權認證的程式碼 以後我再講 先略過。 53 54 //除此之外 ,onTransact 這個方法 就是執行在Binder執行緒池中的,一般就是客戶端發起請求,然後android底層程式碼把這個客戶端發起的 55 //請求 封裝成3個引數 來呼叫這個onTransact方法,第一個引數code 就代表客戶端想要呼叫服務端 方法的 標誌位。 56 //其實也很好理解 服務端可能有n個方法 每個方法 都有一個對應的int值來代表,這個code就是這個int值,用來標示客戶端想呼叫的服務端的方法 57 //data就是方法引數,reply就是方法返回值。都很好理解 58 59 //其實隱藏了很重要的一點,這個方法既然是執行在binder執行緒池中的,所以在這個方法裡面呼叫的伺服器方法也是執行在Binder執行緒池中的, 60 //所以我們要記得 如果你的服務端程式 有可能和多個客戶端相聯的話,你方法裡使用的那些引數 必須要是支援非同步的,否則的話 61 //值就會錯亂了!這點一定要記住!結論就是Binder方法 一定要是同步方法!!!!!! 62 @Override 63 public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { 64 switch (code) { 65 case INTERFACE_TRANSACTION: { 66 reply.writeString(DESCRIPTOR); 67 return true; 68 } 69 case TRANSACTION_getPersonList: { 70 data.enforceInterface(DESCRIPTOR); 71 java.util.List<com.example.administrator.aidlmessagetest.Person> _result = this.getPersonList(); 72 reply.writeNoException(); 73 reply.writeTypedList(_result); 74 return true; 75 } 76 case TRANSACTION_addPerson: { 77 data.enforceInterface(DESCRIPTOR); 78 com.example.administrator.aidlmessagetest.Person _arg0; 79 if ((0 != data.readInt())) { 80 _arg0 = com.example.administrator.aidlmessagetest.Person.CREATOR.createFromParcel(data); 81 } else { 82 _arg0 = null; 83 } 84 this.addPerson(_arg0); 85 reply.writeNoException(); 86 return true; 87 } 88 } 89 return super.onTransact(code, data, reply, flags); 90 } 91 92 //注意這裡的Proxy 這個類名也是不變的,從前文我們知道 只有在多程式通訊的情況下 才會返回這個代理的物件 93 private static class Proxy implements com.example.administrator.aidlmessagetest.IPersonManager { 94 private android.os.IBinder mRemote; 95 96 Proxy(android.os.IBinder remote) { 97 mRemote = remote; 98 } 99 100 @Override 101 public android.os.IBinder asBinder() { 102 return mRemote; 103 } 104 105 public java.lang.String getInterfaceDescriptor() { 106 return DESCRIPTOR; 107 } 108 109 110 //這裡我們一共有2個方法 一個getPersonList 一個addPerson 我們就分析一個方法就可以了 111 //並且要知道 這2個方法執行在客戶端!!!!!!!!!!!!!!!! 112 //首先就是建立了3個物件_data 輸入物件,_reply輸出物件,_result返回值物件 113 //然後把引數資訊 寫入到_data裡,接著就呼叫了transact這個方法 來傳送rpc請求,然後接著 114 //當前執行緒掛起, 服務端的onTransace方法才被呼叫,呼叫結束以後 當前執行緒繼續執行,直到 115 //從_reply中取出rpc的返回結果 然後返回_reply的資料 116 117 //所以這裡我們就要注意了,客戶端發起呼叫遠端請求時,當前客戶端的執行緒就會被掛起了, 118 //所以如果一個遠端方法 很耗時,我們客戶端就一定不能在ui main執行緒裡在發起這個rpc請求,不然就anr了。 119 @Override 120 public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException { 121 android.os.Parcel _data = android.os.Parcel.obtain(); 122 android.os.Parcel _reply = android.os.Parcel.obtain(); 123 java.util.List<com.example.administrator.aidlmessagetest.Person> _result; 124 try { 125 _data.writeInterfaceToken(DESCRIPTOR); 126 mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0); 127 _reply.readException(); 128 _result = _reply.createTypedArrayList(com.example.administrator.aidlmessagetest.Person.CREATOR); 129 } finally { 130 _reply.recycle(); 131 _data.recycle(); 132 } 133 return _result; 134 } 135 136 //你看自動生成binder程式碼的時候 連你的註釋也一起拷貝過來了。。。。。是不是很有趣 137 //關於這個引數in 其實你不加也是可以編譯通過的,這裡我就先加一下 具體引數的意義 以後會說 138 139 @Override 140 public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException { 141 android.os.Parcel _data = android.os.Parcel.obtain(); 142 android.os.Parcel _reply = android.os.Parcel.obtain(); 143 try { 144 _data.writeInterfaceToken(DESCRIPTOR); 145 if ((person != null)) { 146 _data.writeInt(1); 147 person.writeToParcel(_data, 0); 148 } else { 149 _data.writeInt(0); 150 } 151 mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); 152 _reply.readException(); 153 } finally { 154 _reply.recycle(); 155 _data.recycle(); 156 } 157 } 158 } 159 160 static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 161 static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 162 } 163 164 public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException; 165 //關於這個引數in 其實你不加也是可以編譯通過的,這裡我就先加一下 具體引數的意義 以後會說 166 167 public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException; 168 }
到這裡 相信大家 至少在應用層上面,就對Binder就一個很直觀的理解了,對於程式間通訊來說,具體的流程就分為如下幾步:
1.Client 發起遠端呼叫請求 也就是RPC 到Binder。同時將自己掛起,掛起的原因是要等待RPC呼叫結束以後返回的結果
2.Binder 收到RPC請求以後 把引數收集一下,呼叫transact方法,把RPC請求轉發給service端。
3.service端 收到rpc請求以後 就去執行緒池裡 找一個空閒的執行緒去走service端的 onTransact方法 ,實際上也就是真正在執行service端的 方法了,等方法執行結束 就把結果 寫回到binder中。
4.Binder 收到返回資料以後 就喚醒原來的Client 執行緒,返回結果。至此,一次程式間通訊 的過程就結束了
搞明白以後 我們就可以來嘗試著 手下一下Binder:(前面我們aidl 幫我們生成的binder 是人,也就是person,那這次我們自己寫的時候 就用狗吧,用DOG)
首先定義一個Dog.java: 實際上和person 一樣的 所以這裡暫時把程式碼摺疊起來。
1 package com.example.administrator.writebindercodeexample; 2 3 import android.os.Parcel; 4 import android.os.Parcelable; 5 6 /** 7 * Created by Administrator on 2016/1/27. 8 */ 9 public class Dog implements Parcelable { 10 11 public int getGender() { 12 return gender; 13 } 14 15 public String getName() { 16 return name; 17 } 18 19 public void setGender(int gender) { 20 this.gender = gender; 21 } 22 23 public void setName(String name) { 24 this.name = name; 25 } 26 27 private int gender; 28 private String name; 29 30 31 @Override 32 public int describeContents() { 33 return 0; 34 } 35 36 @Override 37 public void writeToParcel(Parcel dest, int flags) { 38 dest.writeInt(this.gender); 39 dest.writeString(this.name); 40 } 41 42 public Dog() { 43 } 44 45 protected Dog(Parcel in) { 46 this.gender = in.readInt(); 47 this.name = in.readString(); 48 } 49 50 public static final Parcelable.Creator<Dog> CREATOR = new Parcelable.Creator<Dog>() { 51 public Dog createFromParcel(Parcel source) { 52 return new Dog(source); 53 } 54 55 public Dog[] newArray(int size) { 56 return new Dog[size]; 57 } 58 }; 59 }
然後寫一個介面IDogManager
1 package com.example.administrator.writebindercodeexample; 2 3 import android.os.IBinder; 4 import android.os.IInterface; 5 import android.os.RemoteException; 6 7 import java.util.List; 8 9 /** 10 * Created by Administrator on 2016/1/27. 11 */ 12 public interface IDogManager extends IInterface { 13 14 static final String DESCRIPTOR = "com.example.administrator.writebindercodeexample.IDogManager"; 15 static final int TRANSACTION_getDogList = IBinder.FIRST_CALL_TRANSACTION + 0; 16 static final int TRANSACTION_addDog = IBinder.FIRST_CALL_TRANSACTION + 1; 17 18 public List<Dog> getDogList() throws RemoteException; 19 20 public void addDog(Dog dog) throws RemoteException; 21 22 }
然後寫我們的binder,注意我們的binder 我這裡是寫的抽象類,因為你寫成實體類的話 就必須要實現IDogManger裡的2個方法 ,然而為了結構清晰 我們並不準備把binder 放在service裡 實現。
所以這裡binder 我們還是用抽象類來做,然後在service裡 實現 getDogList和addDog方法即可。
1 package com.example.administrator.writebindercodeexample; 2 3 import android.os.Binder; 4 import android.os.IBinder; 5 import android.os.Parcel; 6 import android.os.RemoteException; 7 8 /** 9 * Created by Administrator on 2016/1/27. 10 */ 11 public abstract class DogManagerImpl extends Binder implements IDogManager { 12 13 public DogManagerImpl() { 14 this.attachInterface(this, DESCRIPTOR); 15 } 16 17 public static com.example.administrator.writebindercodeexample.IDogManager asInterface(android.os.IBinder obj) { 18 if ((obj == null)) { 19 return null; 20 } 21 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 22 //如果是同1個程式,也就是說程式內通訊的話 我們就返回括號內裡的物件 23 if (((iin != null) && (iin instanceof com.example.administrator.writebindercodeexample.IDogManager))) { 24 return ((com.example.administrator.writebindercodeexample.IDogManager) iin); 25 } 26 //如果不是同一程式,是2個程式之間相互通訊,那我們就得返回這個Stub.Proxy 看上去叫Stub 代理的物件了 27 return new com.example.administrator.writebindercodeexample.DogManagerImpl.Proxy(obj); 28 } 29 30 31 @Override 32 public IBinder asBinder() { 33 return this; 34 } 35 36 @Override 37 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { 38 switch (code) { 39 case INTERFACE_TRANSACTION: { 40 reply.writeString(DESCRIPTOR); 41 return true; 42 } 43 case TRANSACTION_getDogList: { 44 data.enforceInterface(DESCRIPTOR); 45 java.util.List<com.example.administrator.writebindercodeexample.Dog> _result = this.getDogList(); 46 reply.writeNoException(); 47 reply.writeTypedList(_result); 48 return true; 49 } 50 case TRANSACTION_addDog: { 51 data.enforceInterface(DESCRIPTOR); 52 com.example.administrator.writebindercodeexample.Dog _arg0; 53 if ((0 != data.readInt())) { 54 _arg0 = com.example.administrator.writebindercodeexample.Dog.CREATOR.createFromParcel(data); 55 } else { 56 _arg0 = null; 57 } 58 this.addDog(_arg0); 59 reply.writeNoException(); 60 return true; 61 } 62 } 63 return super.onTransact(code, data, reply, flags); 64 } 65 66 private static class Proxy extends DogManagerImpl { 67 private android.os.IBinder mRemote; 68 69 Proxy(android.os.IBinder remote) { 70 mRemote = remote; 71 } 72 73 @Override 74 public android.os.IBinder asBinder() { 75 return mRemote; 76 } 77 78 public java.lang.String getInterfaceDescriptor() { 79 return DESCRIPTOR; 80 } 81 82 @Override 83 public java.util.List<com.example.administrator.writebindercodeexample.Dog> getDogList() throws android.os.RemoteException { 84 android.os.Parcel _data = android.os.Parcel.obtain(); 85 android.os.Parcel _reply = android.os.Parcel.obtain(); 86 java.util.List<com.example.administrator.writebindercodeexample.Dog> _result; 87 try { 88 _data.writeInterfaceToken(DESCRIPTOR); 89 mRemote.transact(DogManagerImpl.TRANSACTION_getDogList, _data, _reply, 0); 90 _reply.readException(); 91 _result = _reply.createTypedArrayList(com.example.administrator.writebindercodeexample.Dog.CREATOR); 92 } finally { 93 _reply.recycle(); 94 _data.recycle(); 95 } 96 return _result; 97 } 98 99 @Override 100 public void addDog(com.example.administrator.writebindercodeexample.Dog dog) throws android.os.RemoteException { 101 android.os.Parcel _data = android.os.Parcel.obtain(); 102 android.os.Parcel _reply = android.os.Parcel.obtain(); 103 try { 104 _data.writeInterfaceToken(DESCRIPTOR); 105 if ((dog != null)) { 106 _data.writeInt(1); 107 dog.writeToParcel(_data, 0); 108 } else { 109 _data.writeInt(0); 110 } 111 mRemote.transact(DogManagerImpl.TRANSACTION_addDog, _data, _reply, 0); 112 _reply.readException(); 113 } finally { 114 _reply.recycle(); 115 _data.recycle(); 116 } 117 } 118 } 119 120 }
到這,我們的手寫binder 就完成了,然後看看 service 以及客戶端 怎麼呼叫。
先看service:
1 package com.example.administrator.writebindercodeexample; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.IBinder; 6 import android.os.RemoteException; 7 import android.util.Log; 8 9 import java.util.ArrayList; 10 import java.util.List; 11 12 public class RemoteService extends Service { 13 14 private List<Dog> mDogsList = new ArrayList<Dog>(); 15 16 private final DogManagerImpl mBinder = new DogManagerImpl() { 17 @Override 18 public List<Dog> getDogList() throws RemoteException { 19 return mDogsList; 20 } 21 22 @Override 23 public void addDog(Dog dog) throws RemoteException { 24 mDogsList.add(dog); 25 } 26 }; 27 28 @Override 29 public IBinder onBind(Intent intent) { 30 return mBinder; 31 } 32 }
然後看看 啟動如何在客戶端bind 這個service:
1 private IDogManager mService; 2 3 private ServiceConnection sc = new ServiceConnection() { 4 @Override 5 public void onServiceConnected(ComponentName name, IBinder service) { 6 mService = DogManagerImpl.asInterface(service); 7 8 } 9 10 @Override 11 public void onServiceDisconnected(ComponentName name) { 12 mService = null; 13 } 14 };
到這 就基本寫完了,手寫binder的好處就是 你可以自己在binder方法裡 寫一些log,能夠更加深刻的認識到 Binder 作為 程式間通訊 媒介的重要作用以及原理。
熟悉以後,還是用aidl 的方法 自動生成程式碼 最好。