手寫Binder實現android中的程序間通訊
在Android系統中,每個應用都執行在一個程序上,具有自己的DVM例項,而且程序之間是相互隔離的,也就是說各個程序之間的資料是互相獨立,互不影響的,而如果一個程序崩潰了,也不會影響到另一個程序。
為什麼採取這樣的設計呢,比如這樣的前提下將互相不影響的系統功能分拆到不同的程序裡面去,有助於提升系統的穩定性,畢竟我們都不想自己的應用程序崩潰會導致整個手機系統的崩潰。
程序之間隔離是不錯的選擇,可是如果程序之間想要互相通訊,進行資料互動的時候那該怎麼辦呢?例如我們在自己的應用中想要訪問手機通訊錄中的聯絡人,很顯然這是兩個不同的程序,如果Android沒有提供一種程序之間交流的機制,那麼這種功能將無法實現,不過由於Android系統使用的是Linux核心,而在Linux系統中程序之間的互動是有一套機制的,所以Android也借鑑了其中的一些機制,從而形成了Android的IPC機制,其意思就是程序間的通訊,也就是兩個程序之間的通訊過程。
下面我們通過小例子看看;
1、新建一個Person類,並宣告一個靜態變數:
public class Person { public static String name="張三"; }
2、在MainActivity的onCreate方法中修改name的值,並列印log
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d("MainActivity", "原名:" + Person.name); person.name = "李四"; Log.d("MainActivity", "修改後:" + Person.name); }
3、將TestActivity設定為新程序,並在其onCreate方法中訪問name
<activity android:name=".TestActivity" android:process=":second"> </activity> public class TestActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); Log.d("TestActivity" , Person.name); } }
執行看看結果:

1112.png
TestActivity中獲取到了name值並未修改。

1113.png
通過以上的例子,大家應該明白了一點:在不同的程序之間訪問同一個靜態變數是行不通的。其原因是:每一個程序都分配有一個獨立的虛擬機器,不同的虛擬機器在記憶體分配上有不同的地址空間,這就導致在不同的虛擬機器上訪問同一個物件會產生多個副本。例如我們在MainActivity中訪問的name的值只會影響當前程序,而對其他程序不會造成影響,所以在TestActivity中訪問name時依舊只能訪問自己程序中的副本。
Android解決IPC的方法中有一種是AIDL,它使用的原理就是Binder,只有理解了Binder,我們才算是理解了Android跨程序通訊的原理。在這裡我會帶大家看看Android中有哪一些重要的地方使用到了Binder,接著我們會通過一個例項來了解如何使用Binder。
Binder在Android中的運用
說起Binder在Android的使用場景,可以說是無處不在,我列出一些最常見的場景:
四大元件的生命週期都是使用Binder機制進行管理的,比如AMS,PMS,PMS,
View的工作原理也使用了Binder
WindowManager的工作機制同樣使用了Binder
接下來我們通過傳統的AIDL實現程序間通訊。
1、建立Person.java,Person.aidl,IPersonManager.aidl
public class Person implements Parcelable { public String name = "張三"; public Person() { } public Person(String name) { this.name = name; } public Person(Parcel in) { this.name = in.readString(); } public static final Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.name); } }
建立完畢之後手動編譯專案(Build-->make Project),接著就會在
app/build/generated/source/aidl/debug/com/binder/IPersonManager.java中看到自動生成的IStudentManager介面

1114.png
2、分析IPersonManager.java
public interface IPersonManager extends android.os.IInterface { /** * 內部類Stub,繼承自Binder並且實現了IStudentManager介面,因此他也是一個Binder物件,這個內部類是需要在服務端手動實現的,並且會通過onBind方法返回給客戶端 * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.binder.IPersonManager { /** * 唯一的binder標示 可以看到就是IPersonManager的全路徑名 */ private static final java.lang.String DESCRIPTOR = "com.binder.IPersonManager"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * 將服務端的Binder物件轉換為客戶端的所需的AIDL介面型別的物件,客戶端拿到這個物件就可以通過這個物件遠端訪問服務端的方法 * Cast an IBinder object into an com.binder.IPersonManager interface, * generating a proxy if needed. */ public static com.binder.IPersonManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.binder.IPersonManager))) { return ((com.binder.IPersonManager) iin); } return new com.binder.IPersonManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } /** * 執行在服務端程序的Binder執行緒池中;當客戶端程序發起遠端請求時,遠端請求會要求系統底層執行回撥該方法 * @param code 客戶端程序請求方法識別符號。服務端程序會根據該標識確定所請求的目標方法 * @param data 目標方法的引數,他是客戶端程序傳進來的,當我們呼叫addPerson(Person person)方法時,引數就是Person物件 * @param reply 目標方法執行後的結果,將會返回給客戶端,例如當我們呼叫getPersonList,返回的就是一個Person的列表 */ @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_getPersonList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.binder.Person> _result = this.getPersonList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addPerson: { data.enforceInterface(DESCRIPTOR); com.binder.Person _arg0; if ((0 != data.readInt())) { _arg0 = com.binder.Person.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addPerson(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } /** * 代理的內部類,他實現了IStudentManager介面,這個代理類就是服務端返回給客戶端的AIDL介面物件,客戶端可以通過這個代理類訪問服務端的方法 */ private static class Proxy implements com.binder.IPersonManager { 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; } /** *這裡我們一共有2個方法 一個getPersonList 一個addPerson 我們就分析一個方法就可以了 *並且要知道 這2個方法執行在客戶端!!!!!!!!!!!!!!!! *首先就是建立了3個物件_data 輸入物件,_reply輸出物件,_result返回值物件 *然後把引數資訊 寫入到_data裡,接著就呼叫了transact這個方法 來發送rpc請求,然後接著 *當前執行緒掛起, 服務端的onTransace方法才被呼叫,呼叫結束以後 當前執行緒繼續執行,直到 *從_reply中取出rpc的返回結果 然後返回_reply的資料 *所以這裡我們就要注意了,客戶端發起呼叫遠端請求時,當前客戶端的執行緒就會被掛起了, *所以如果一個遠端方法 很耗時,我們客戶端就一定不能在ui main執行緒裡在發起這個rpc請求,不然就anr了。 * @return * @throws android.os.RemoteException */ @Override public java.util.List<com.binder.Person> getPersonList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.binder.Person> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.binder.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addPerson(com.binder.Person person) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((person != null)) { _data.writeInt(1); person.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.util.List<com.binder.Person> getPersonList() throws android.os.RemoteException; public void addPerson(com.binder.Person person) throws android.os.RemoteException; }
對於程序間通訊來說,具體的流程就分為如下幾步:
1.Client 發起遠端呼叫請求 也就是RPC 到Binder。同時將自己掛起,掛起的原因是要等待RPC呼叫結束以後返回的結果
2.Binder 收到RPC請求以後 把引數收集一下,呼叫transact方法,把RPC請求轉發給service端。
3.service端 收到rpc請求以後 就去執行緒池裡 找一個空閒的執行緒去走service端的 onTransact方法 ,實際上也就是真正在執行service端的 方法了,等方法執行結束 就把結果 寫回到binder中。
4.Binder 收到返回資料以後 就喚醒原來的Client 執行緒,返回結果。至此,一次程序間通訊 的過程就結束了
建立RemoteService,模擬服務端;
public class RemoteService extends Service { //適合用於程序間傳輸的列表類 private CopyOnWriteArrayList<Person> mPersonsList = new CopyOnWriteArrayList<>(); public final IPersonManager.Stub mBinder = new IPersonManager.Stub() { @Override public List<Person> getPersonList() throws RemoteException { return mPersonsList; } @Override public void addPerson(Person person) { mPersonsList.add(person); } }; @Override public void onCreate() { super.onCreate(); mPersonsList.add(new Person("小明")); mPersonsList.add(new Person("小李")); mPersonsList.add(new Person("小華")); Log.d(RemoteService.class.getSimpleName(), "RemoteService 啟動了"); } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; }
}
在客戶端繫結Service!
public class TestActivity extends AppCompatActivity { public static String TAG = TestActivity.class.getSimpleName(); private IPersonManager mPersonManager; private ServiceConnection mConnection = new ServiceConnection() { //onServiceConnected與onServiceDisconnected都是在主執行緒中的,所以如果裡面如果涉及到服務端的耗時操作那麼需要在子執行緒中進行 @Override public void onServiceConnected(ComponentName name, IBinder service) { //獲取到IPersonManager物件 mPersonManager = com.binder.IPersonManager.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mPersonManager = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); Intent intent = new Intent(this, RemoteService.class); bindService(intent, mConnection, BIND_AUTO_CREATE); } /** * 在客戶端向服務端新增Person * * @param view */ public void addPerson(View view) { if (mPersonManager != null) { try { Person mPerson1 = new Person("王五"); mPersonManager.addPerson(mPerson1); Log.d(TAG, "新增一位學生:" + mPerson1.name); Person mPerson2 = new Person("趙六"); mPersonManager.addPerson(mPerson2); Log.d(TAG, "新增一位學生:" + mPerson2.name); Person mPerson3 = new Person("麻子"); mPersonManager.addPerson(mPerson3); Log.d(TAG, "新增一位學生:" + mPerson3.name); } catch (Exception e) { e.printStackTrace(); } } } /** * 在客戶端向服務端發起查詢學生的請求 * * @param view */ public void getPerson(View view) { //由於服務端的查詢操作是耗時操作,所以客戶端需要開啟子執行緒進行工作 new Thread(new Runnable() { @Override public void run() { if (mPersonManager != null) { try { final List<Person> students = mPersonManager.getPersonList(); for (int i = 0; i < students.size(); i++) { Log.d(TAG, "從伺服器獲取到學生:" + students.get(i).name); } } catch (Exception e) { e.printStackTrace(); } } } }).start(); }
}
新增資料:

1116.png
查詢服務端資料

1117.png
通過上面的案例大概明白,分別是Activity充當客戶端,服務端的RemoteService,充當服務管理者的IPersonManager以及充當訪問介質的Binder驅動。他們的職責如下:
RemoteService: 服務提供者,這裡面會有許多我們常用的服務,在本 例中提供的服務就是新增學生以及獲取學生列表。而在系統中則包括有ActivityService 、 WindowMananger等服務,這些系統服務提供的功能,對四大元件以及Window的工作提供的保障。
Activity: 服務呼叫者,一般就是我們的應用,在這裡我們通過呼叫RemoteService的服務來完成工作。
IPersonManager: 他是負責管理服務的,在其內部通過Map集合來儲存Service與Binder的對映關係,這樣客戶端在向其請求服務的時候就能夠返回特定的Binder。
Binder驅動: 他是IPersonManager連線各種Service的橋樑,同時也是客戶端與服務端交流的橋樑。
連串起來就是客戶端(Activity)首先向IPersonManager傳送請求RemoteService的服務,IPersonManager檢視已經註冊在裡面的服務的列表,找到相應的服務後,通過Binder驅動將其中的Binder物件返回給客戶端,從而完成對服務的請求。
接下來,我們就可以來嘗試著手寫一下Binder:
寫一個介面IPersonManager
public interface IPersonManager extends IInterface { List<Person> getPersonList() throws RemoteException; void addPerson(Person person) throws RemoteException; }
寫一個繼承自Binder的抽象類PersonManagerImpl並實現IPersonManager介面,
public abstract class PersonManagerImpl extends Binder implements IPersonManager { //唯一標識用於註冊該BInder,用包名+介面名定義 private static final String DESCRIPTOR = "com.binder.aidl.IPersonManager"; //getList方法唯一標識 static final int TRANSACTION_getList = (IBinder.FIRST_CALL_TRANSACTION + 0); //add方法唯一標識 static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 1); public PersonManagerImpl() { //註冊該binder this.attachInterface(this, DESCRIPTOR); } public static IPersonManager asInterface(IBinder obj) { if ((obj == null)) { return null; } IInterface iin = obj.queryLocalInterface(DESCRIPTOR); //查詢當前程序 if (((iin != null) && (iin instanceof PersonManagerImpl))) { return (PersonManagerImpl) iin;//當前程序返回IBookManager } return new Proxy(obj);//非當前程序返回Proxy } @Override protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getList: { data.enforceInterface(DESCRIPTOR); List<Person> _result = this.getPersonList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_add: { data.enforceInterface(DESCRIPTOR); Person person; if ((0 != data.readInt())) { person = Person.CREATOR.createFromParcel(data); } else { person = null; } this.addPerson(person); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } @Override public IBinder asBinder() { return this; } public static class Proxy implements IPersonManager { private IBinder mRemote; public String getInterfaceDescriptor() { return DESCRIPTOR; } public Proxy(IBinder obj) { this.mRemote = obj; } @Override public List<Person> getPersonList() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); List<Person> result; try { data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_getList, data, reply, 0); reply.readException(); result = reply.createTypedArrayList(Person.CREATOR); } finally { reply.recycle(); data.recycle(); } return result; } @Override public void addPerson(Person person) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(DESCRIPTOR); if ((person != null)) { data.writeInt(1); person.writeToParcel(data, 0); } else { data.writeInt(0); } mRemote.transact(TRANSACTION_add, data, reply, 0); reply.readException(); } finally { reply.recycle(); data.recycle(); } } @Override public IBinder asBinder() { return null; } }
}
我們的手寫binder 就完成了,然後看看 service 以及客戶端 怎麼呼叫。
public class RemoteService extends Service { public final PersonManagerImpl mBinder = new PersonManagerImpl() { @Override public List<Person> getPersonList() throws RemoteException { return mPersonsList; } @Override public void addPerson(Person person) { mPersonsList.add(person); } }; //適合用於程序間傳輸的列表類 private CopyOnWriteArrayList<Person> mPersonsList = new CopyOnWriteArrayList<>(); @Override public void onCreate() { super.onCreate(); mPersonsList.add(new Person("小明")); mPersonsList.add(new Person("小李")); mPersonsList.add(new Person("小華")); Log.d(RemoteService.class.getSimpleName(), "RemoteService 啟動了"); } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; }
}
啟動 Service:
private ServiceConnection mConnection = new ServiceConnection() { //onServiceConnected與onServiceDisconnected都是在主執行緒中的,所以如果裡面如果涉及到服務端的耗時操作那麼需要在子執行緒中進行 @Override public void onServiceConnected(ComponentName name, IBinder service) { //獲取到IPersonManager物件 mPersonManager = PersonManagerImpl.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mPersonManager = null; } };
到這 就基本寫完了