1. 程式人生 > >Android IPC通訊之Binder機制分析

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();
        }
    };
}