概述
在利用Binder進行IPC的時候,會經常需要建立一個Server端,Android中通常的實現是利用Service來實現,所以再進行IPC之前先了解之前先複習一下Service:
啟動方式
startService
Intent intent = new Intent();
intent.setClass(this, MyService.class);
startService(intent);
複製程式碼
bindService
Intent intent = new Intent();
intent.setClass(this, MyService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//繫結成功的回撥
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
//斷開連線的回撥
}
}, Context.BIND_AUTO_CREATE);
複製程式碼
生命週期

常見方法回撥時機
onCreate()
在每個service的生命週期中這個方法會且僅會呼叫一次,並且它的呼叫在onStartCommand()以及onBind()之前,我們可以在這個方法中進行一些一次性的初始化工作。
onStartCommand()
當其他元件通過startService()方法啟動service時,此方法將會被呼叫。
onBind()
當其他元件通過bindService()方法與service相繫結之後,此方法將會被呼叫。這個方法有一個IBinder的返回值,這意味著在重寫它的時候必須返回一個IBinder物件,它是用來支撐其他元件與service之間的通訊的——另外,如果你不想讓這個service被其他元件所繫結,可以通過在這個方法返回一個null值來實現。
onUnbind()
當呼叫UnBindService的時候 ,此方法會被呼叫
onDestroy
這是service一生中呼叫的最後一個方法,當這個方法被呼叫之後,service就會被銷燬。所以我們應當在這個方法裡面進行一些資源的清理,比如註冊的一些監聽器什麼的。
銷燬方式
startService方式啟動
startService()啟動,stopService()銷燬的生命週期如下: ->onCreate()->onStartCommand()->Service running-> onDestroy()
bindService方式啟動
bindService()啟動,unbindService()銷燬的生命週期如下: ->onCreate()->onStartCommand()->Service running-> onDestroy()
先startService再bindService
- 如果結束只呼叫unbindService(),那麼只會執行到onUnbind(),將不會執行onDestroy():->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind()。
- 如果在unbindService後,在呼叫stopService(),那麼:->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind()->onDestroy() 。
正文
startService
其實starService跟Binder機制沒有太大關係,通過此方式雖然可以啟動一個本地或者遠端的Service,但是我們拿不到Binder物件,不能直接通過AIDL的方式進行遠端通訊,只能通過其它的IPC方式進行通訊,即時如此,這種方式啟動的Service還是有一個方法需要注意一下,就是onStartCommand。
onStartCommand
@Override public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
複製程式碼
呼叫時機
啟動service的時候,onCreate方法只有第一次會呼叫,onStartCommand每次都被呼叫。onStartCommand會告訴系統如何重啟服務,如判斷是否異常終止後重新啟動,在何種情況下異常終止 啟動服務時依次執行onCreate,onStartCommand;如果在系統顯示呼叫stopService和stopSelf之前終止服務,service再次重啟,onStartCommand會被呼叫,重啟服務時依次執行onStartCommand。
返回值
我們發現onStartCommand這個方法有個int型別返回值,實際上有四種型別,都是定義在Service中的靜態常量:
public static final int START_NOT_STICKY = 2;
public static final int START_REDELIVER_INTENT = 3;
public static final int START_STICKY = 1;
public static final int START_STICKY_COMPATIBILITY = 0;
複製程式碼
下面依次解釋下這幾種返回值的含義:
- 1):START_STICKY:如果service程式被kill掉,保留service的狀態為開始狀態,但不保留遞送的intent物件。隨後系統會嘗試重新建立service,由於服務狀態為開始狀態,所以建立服務後一定會呼叫onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到service,那麼引數Intent將為null。
- 2):START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啟該服務
- 3):START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啟該服務,並將Intent的值傳入。
- 4):START_STICKY_COMPATIBILITY:START_STICKY的相容版本,但不保證服務被kill後一定能重啟。
flags
flags表示啟動服務的方式:通過startService啟動時為0;onStartCommand返回為START_FLAG_REDELIVERY, or START_FLAG_RETRY.
- START_FLAG_RETRY:表示服務之前被設為START_STICKY,則會被傳入這個標記。
- START_FLAG_REDELIVERY:如果你實現onStartCommand()來安排非同步工作或者在另一個執行緒中工作, 那麼你可能需要使用START_FLAG_REDELIVERY來讓系統重新傳送一個intent。這樣如果你的服務在處理它的時候被Kill掉, Intent不會丟失.
bindService
前面說了那麼多,bindService才是重點,通過bindService啟動的Service會呼叫onBind方法,我們現在 分析一下如何拿到我們想要的Binder物件,因為不管是Client還是Server,想要通過AIDL進行IPC通訊,就必須拿到一個Binder物件,但是如果通過Messenger的話就不需要了,因為Messenger底層自己封裝了AIDL。 其實通過前面的分析,很容易看出Client在進行bindService的時候傳入了一個ServiceConnection,當跟Server端連線成功的時候會在onServiceConnected中返回一個Binder物件,那麼這個Binder物件是從哪兒來的呢?結合Server端的onBind方法,就很明顯了,這個Binder物件就是服務端傳遞過來的。其實通過之前的AIDL分析,也很容易能夠判斷出來,只要是通過Binder機制進行IPC通訊的,無論是Client還是Server端,都會涉及到Binder,我們只要找到了Binder,整個流程就很清晰了,下面分別描述一下Client跟Server中關於Binder的兩個方法
CLient的bindService
ServiceConnection
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
複製程式碼
Client通過onServiceConnected可以拿到服務端返回的Binder物件,因為Binder實現了IBinder介面,返回的是IBinder。
Server的onBind
@Override
public IBinder onBind(Intent intent) {
return null;
}
複製程式碼
Server通過onBind返回IBinder物件,預設的而實現為null,我們有多種方式可以實現Binder,通過繼承Binder類,AIDL以及Messenger都可以做到,下面簡要說明一下自定義Binder,Messenger跟AIDL下面會重點進行講解:
繼承Binder
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
複製程式碼
比較簡單,這種方式一般用在Service跟Activity進行通訊的過程中,進行方法呼叫,大多數是在同一個程式中,我們看到在LocalBinder 中定義了getService方法,可以獲取到Service的例項,比如我在專案中的定位是放在Service中的,但是拿到定位資料之後需要在Activity中顯示地域切換的對話方塊,所以Service就需要跟Activity進行互動,Service可以調Activity的方法,同樣由於Activity在Binder中拿到了Service的引用,也可以呼叫Service的中的方法。
使用AIDL進行IPC
Client端程式碼
ServiceConnection
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mPeopleManager = PeopleManager.Stub.asInterface(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
複製程式碼
開始連線
private void attemptToBindService() {
Intent intent = new Intent();
intent.setClass(this, AIDLService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
複製程式碼
addPeople
public void addBook(View view) {
//如果與服務端的連線處於未連線狀態,則嘗試連線
if (!mBound) {
attemptToBindService();
return;
}
if (mPeopleManager == null)
return;
People people = new People();
people.setAge(18);
people.setGender("male");
people.setHobby("travel");
try {
mPeopleManager.addPeople(people);
Log.e(getLocalClassName(), people.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
複製程式碼
getPeople
public void getPeople(View view) {
//如果與服務端的連線處於未連線狀態,則嘗試連線
if (!mBound) {
attemptToBindService();
Log.d("client-->", "正在連線Server");
return;
}
if (mPeopleManager == null)
return;
try {
Toast.makeText(this, String.valueOf(mPeopleManager.getPeople().size()), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
複製程式碼
Server端程式碼
建立一個Stub物件
//由AIDL檔案生成的PeopleManager
private final PeopleManager.Stub mPeopleManager = new PeopleManager.Stub() {
@Override
public List<People> getPeople() throws RemoteException {
synchronized (this) {
if (mPeoples != null) {
return mPeoples;
}
return new ArrayList<>();
}
}
@Override
public void addPeople(People book) throws RemoteException {
synchronized (this) {
if (mPeoples == null) {
mPeoples = new ArrayList<>();
}
if (book == null) {
Log.e(TAG, "People is null in In");
}
mPeoples.add(book);
}
}
};
複製程式碼
onBind進行返回
@Override
public IBinder onBind(Intent intent) {
return mPeopleManager;
}
複製程式碼
開啟遠端執行緒
<service
android:name=".AIDLService"
android:enabled="true"
android:exported="true"
android:process=":server">
</service>
複製程式碼
測試

使用Messenger進行IPC

Client端程式碼
建立一個Handler
public class ClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MESS_FROM_SERVER:
//接收伺服器傳過來的值
Bundle bundle = msg.getData();
int peopleSize = bundle.getInt("server");
Toast.makeText(MessengerActivity.this, String.valueOf(peopleSize), Toast.LENGTH_SHORT).show();
break;
}
}
}
複製程式碼
建立一個Messenger
Messenger messenger = new Messenger(new ClientHandler());
複製程式碼
ServiceConnection
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBound = true;
mService = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
isBound = false;
}
};
複製程式碼
開始連線
private void attemptToBindService() {
Intent intent = new Intent();
intent.setClass(this, MessengerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
複製程式碼
addPeople
public void addPeople(View view) {
//如果與服務端的連線處於未連線狀態,則嘗試連線
if (!isBound) {
attemptToBindService();
return;
}
People people = new People();
people.setAge(18);
people.setGender("male");
people.setHobby("travel");
Message message = Message.obtain(null, MESS_ADD_PEOPLE);
Bundle bundle = new Bundle();
bundle.putParcelable("people", people);
message.setData(bundle);
//Messenger用來接收服務端發來的資訊
message.replyTo = messenger;
try {
//將Message傳送到服務端
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
複製程式碼
getPeople
public void getPeople(View view) {
//如果與服務端的連線處於未連線狀態,則嘗試連線
if (!isBound) {
attemptToBindService();
Log.d("client-->", "正在連線Server");
return;
}
Message message = Message.obtain(null, MESS_GET_PEOPLE);
//Messenger用來接收服務端發來的資訊
message.replyTo = messenger;
try {
//將Message傳送到服務端
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
複製程式碼
Server端程式碼
建立一個Handler 用來收發訊息,由於我們是在一個應用裡面的開啟的多程式,所以這邊接收到Client的請求,不能直接將其轉化為People物件,因為People物件屬於應用程式,而MessengerService屬於另外一個程式,是不能共享這個資料的,這裡採用了收到訊息後,用一個int 型別的資料來模擬集合的數量
mHandler = new Handler() {
@Override
public void handleMessage(Message msgFromClient) {
super.handleMessage(msgFromClient);
//獲取一個新訊息
Message replyToClient = Message.obtain(msgFromClient);
switch (msgFromClient.what) {
//根據Message.what來判斷執行服務端的哪段程式碼
case MESS_ADD_PEOPLE:
size += 2;
break;
case MESS_GET_PEOPLE:
replyToClient.what = MESS_FROM_SERVER;
Bundle serverBundle = new Bundle();
serverBundle.putInt("server", size);
replyToClient.setData(serverBundle);
try {
msgFromClient.replyTo.send(replyToClient);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
複製程式碼
建立一個Messenger
messenger = new Messenger(mHandler);
複製程式碼
onBind進行返回
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
複製程式碼
開啟遠端執行緒
<service
android:process=":server"
android:name=".MessengerService"/>
複製程式碼
測試結果

###總結
通過利用AIDL跟Messenger來實現Android應用層的IPC,可以更加方便的幫助我們理解Binder機制,當然Android系統還可以利用其它的方式來進行IPC,例如通過檔案共享,intent傳值等,下面簡單就這些方式做一個對比:
程式間通訊的方式
名稱 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
Intent | 簡單易用 | 只能傳輸Bundle所支援的資料型別 | 四大元件間的程式間通訊 |
檔案共享 | 簡單易用 | 不適合高併發 | 簡單的資料共享,無高併發場景 |
AIDL | 功能強大,支援一對多併發實時通訊 | 使用稍微複雜,需要注意執行緒同步 | 複雜的程式間呼叫,Android中最常用 |
Messenger | 比AIDL稍微簡單易用些 | 比AIDL功能弱,只支援一對多序列實時通訊 | 簡單的程式間通訊 |
ContentProvider | 強大的資料共享能力,可通過call方法擴充套件 | 受約束的AIDL,主要對外提供資料線的CRUD操作 | 程式間的大量資料共享 |
RemoteViews | 在跨程式訪問UI方面有奇效 | 比較小眾的通訊方式 | 某些特殊的場景 |
Socket | 跨主機,通訊範圍廣 | 只能傳輸原始的位元組流 | 常用於網路通訊中 |