Android IPC通訊之Binder機制分析
優勢:
與Linux中的Pipe管道、訊號Signal、訊息佇列Message、共享記憶體Share Memory、Socket插口等相比較,Binder在程序間傳輸資料,只需要執行一次拷貝操作。因此它不僅提高了效率,而且節省了記憶體空間。
角色:
- Server: 提供服務的程序稱為server程序。
- Client: 使用服務的程序稱為client程序。
- Binder驅動: 提供裝置檔案/dev/binder與使用者空間互動,Client、Server和Service Manager通過open和ioctl檔案操作函式與Binder驅動程式進行通訊。Client和Server之間的程序間通訊通過Binder驅動程式間接實現。
- Service Manager: 一個守護程序,用來管理Server,並向Client提供查詢Server介面的能力
互動過程:
1.Server程序,先通過ServiceManager註冊服務,實際上是寫入Binder驅動和儲存到serviceInfo(已經註冊的服務列表)。
2.Client程序在訪問Server服務之前,先通過ServiceManager查詢獲取到它的一個BinderProxyd物件,然後通過這個Binder代理介面向它傳送程序間通訊請求,呼叫transact(),寫入相關資訊。
3.在server程序中,每個服務都對應一個Binder物件,它通過一個stub來等待Client程序發來程序間通訊請求,觸發onTransact(),獲取到詳細資料。
Binder機制分析如下:
1. ServiceManager類的代理,實現註冊和查詢服務:
接下來檢視,如何建立一個SericeManager的代理類,客戶端通過該代理類進行通訊:
檢視ServiceManager原始碼:
public final class ServiceManager { private static IServiceManager sServiceManager; private static IServiceManager getIServiceManager() { if (sServiceManager != null) { return sServiceManager; } // 獲取到代理物件 sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject()); return sServiceManager; } }
檢視,BinderInternal類
public class BinderInternal {
//從native獲取到對應的指標
public static final native IBinder getContextObject();
}
檢視,ServiceManagerNative原始碼:
public abstract class ServiceManagerNative extends Binder implements IServiceManager{
/**
* Cast a Binder object into a service manager interface, generating
* a proxy if needed.
*/
static public IServiceManager asInterface(IBinder obj){
if (obj == null) {
return null;
}
IServiceManager in =
(IServiceManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}
public IBinder asBinder(){
return this;
}
}
class ServiceManagerProxy implements IServiceManager {
private IBinder mRemote;
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
}
public IBinder asBinder() {
return mRemote;
}
//......省略部分原始碼
}
ServiceManagerNative類似服務端的stub類,用於接受到Client端發來的資料,進行操作。
ServiceManagerProxy是客戶端的proxy類,通過BinderProxy物件(即mRemote物件)進行遠端通訊。
Server程序通過ServiceManager進行addService()
註冊服務或者Client程序通過ServiceManager進行getService(String name)
查詢服務等操作,實際上都是通過ServiceManagerProxy中的mRemote跨程序操作的。
這裡不介紹Binder驅動,涉及 C++層比較繁瑣,相關方面,自行百度理解。
編寫常見的AIDL案例,分析Binder機制
編寫一個aidl檔案:
package com.xingen.remoteservice;
import com.xingen.remoteservice.bean.ProcessBean;
// Declare any non-default types here with import statements
//使用 Android Studio,增量編譯幾乎會立即生成 Binder 類
//在.aidl檔案中定義一些方法
interface CommonAidlInterface {
/**
* 獲取一個隨機數的字串
*/
String getRandomNumberStr();
/**
* 獲取遠端服務返回的物件,注意點:需要import匯入該物件
*/
ProcessBean getRemoteServiceObject();
}
生成對應的java:
package com.xingen.remoteservice;
// Declare any non-default types here with import statements
//使用 Android Studio,增量編譯幾乎會立即生成 Binder 類
//在.aidl檔案中定義一些方法
public interface CommonAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements CommonAidlInterface
{
private static final String DESCRIPTOR = "com.xingen.remoteservice.CommonAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.xingen.remoteservice.CommonAidlInterface interface,
* generating a proxy if needed.
*/
public static CommonAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof CommonAidlInterface))) {
return ((CommonAidlInterface)iin);
}
return new 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_getRandomNumberStr:
{
data.enforceInterface(DESCRIPTOR);
String _result = this.getRandomNumberStr();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getRemoteServiceObject:
{
data.enforceInterface(DESCRIPTOR);
com.xingen.remoteservice.bean.ProcessBean _result = this.getRemoteServiceObject();
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements CommonAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public String getRandomNumberStr() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getRandomNumberStr, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* 獲取遠端服務返回的物件,注意點:需要import匯入該物件
*/
@Override public com.xingen.remoteservice.bean.ProcessBean getRemoteServiceObject() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.xingen.remoteservice.bean.ProcessBean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getRemoteServiceObject, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.xingen.remoteservice.bean.ProcessBean.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getRandomNumberStr = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getRemoteServiceObject = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public String getRandomNumberStr() throws android.os.RemoteException;
/**
* 獲取遠端服務返回的物件,注意點:需要import匯入該物件
*/
public com.xingen.remoteservice.bean.ProcessBean getRemoteServiceObject() throws android.os.RemoteException;
}
CommonAidlInterface介面:
CommonAidlInterface介面是IInteface的子介面,用於Stub類和Proxy的通用父類介面。
Stub類:
Stub類是Binder的子類,用於Server程序中,onTransact()
方法中接收傳遞過來的資訊。
Proxy類:
Proxy中有一個IBinder型別的mRemote物件,它實際上是一個BinderProxy物件(通過ServiceManager)。BinderProxy是一個Java服務代理物件,實現了IBinder介面。
2. Client端的Proxy代理(實際包含BinderProxy物件)資訊傳遞:
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//Stub.asInterface(service)轉成對應的服務介面
remoteServiceInterface = CommonAidlInterface.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
remoteServiceInterface = null;
}
};
繫結Service會返回一個IBinder物件,實際上是BinderProxy類,該物件是查詢ServiceManager獲取到的。
public static CommonAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//本地程序會走這一步
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof CommonAidlInterface))) {
return ((CommonAidlInterface)iin);
}
//遠端程序會返回Proxy
return new Proxy(obj);
}
private static class Proxy implements CommonAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
}
Stub.asInterface(iBinder)
是將BinderProxy物件封裝到一個代理Proxy中,用於更好操作。
跨程序間的傳遞資訊的呼叫:
remoteServiceInterface.getRemoteServiceObject();
實際上呼叫的是Proxy中的getRemoteServiceObject():
@Override public com.xingen.remoteservice.bean.ProcessBean getRemoteServiceObject() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.xingen.remoteservice.bean.ProcessBean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getRemoteServiceObject, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.xingen.remoteservice.bean.ProcessBean.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
會呼叫BinderProxy的transact()方法,傳入資料和結果的parcel物件,會寫入Binder驅動中。
3. Server端的Stub(Binder子類)接受到遠端的資訊:
Binder驅動監聽到Client端遠端資訊,會觸發Binder中的onTransact()方法。
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
case TRANSACTION_getRandomNumberStr:
{
data.enforceInterface(DESCRIPTOR);
//呼叫該方法
String _result = this.getRandomNumberStr();
reply.writeNoException();
//寫入返回結果
reply.writeString(_result);
return true;
}
case TRANSACTION_getRemoteServiceObject:
{
data.enforceInterface(DESCRIPTOR);
com.xingen.remoteservice.bean.ProcessBean _result = this.getRemoteServiceObject();
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
最後,響應到service中的Stub匿名內部類中,呼叫各種對應的方法。
public class CommonRemoteService extends Service {
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,TAG+" 被繫結");
return mBinder;
}
/**
* 獲取程序資訊的對應實體
* @return
*/
private ProcessBean getProcessBean(){
return ProcessUtils.getProcess(this.getApplicationContext(),ProcessUtils.getCurrentProcessId());
}
/**
* 獲取一個隨機字串
* @return
*/
private String getRandomUUIDStr(){
return UUID.randomUUID().toString();
}
/**
* 建立一個CommonAidlInterface.aidl對應的CommonAidlInterface.java中的Stub介面
*
* 用於與遠端服務通訊,這裡是本類(CommonRemoteService)通訊
*/
private final CommonAidlInterface.Stub mBinder=new CommonAidlInterface.Stub() {
@Override
public String getRandomNumberStr() throws RemoteException {
return CommonRemoteService.this.getRandomUUIDStr();
}
@Override
public ProcessBean getRemoteServiceObject() throws RemoteException {
return CommonRemoteService.this.getProcessBean();
}
};
}