Android點將臺:金科玉律[-AIDL-]

張風捷特烈發表於2019-02-25

個人所有文章整理在此篇,將陸續更新收錄:知無涯,行者之路莫言終(我的程式設計之路)

零、前言

本文接Android點將臺:絕命暗殺官[-Service-]篇,本文圖例見
本文通過AIDL實現了跨程式間呼叫Service(即App2呼叫App1的Service)
本篇將來探索一下AIDL自動生成的類,再重新審視一下ActivityManagerService


一、上一篇中AIDL自動生成的IMusicPlayerService分析

1.IMusicPlayerService的類結構

太多了,有點晃眼,開啟結構圖來分析一下

類結構.png


IMusicPlayerService.png


2.從IInterface開始

IMusicPlayerService是一個介面,並且繼承於IInterface介面,那IInterface是何許人也?
就這兩行程式碼,只有一個asBinder方法,返回一個IBinder物件,注意它在的包是android.os

package android.os;
public interface IInterface{
    public IBinder asBinder();
}
複製程式碼

3.IMusicPlayerService的分析

可見除去Stub,IMusicPlayerService裡只是我們在IMusicPlayerService.aidl定義的介面
也沒有什麼太多要說明的地方,我們的終極目標是看一下它的實現類是什麼

/*
 * This file is auto-generated.  DO NOT MODIFY.
    自動生成的,不要修改
 * Original file: J:\\Java\\Android\\LeverUp\\TolyService\\app\\src\\main\\aidl\\com\\toly1994\\tolyservice\\IMusicPlayerService.aidl
 原始檔案在: J:\\Java\\Android\\LeverUp\\TolyService\\app\\src\\main\\aidl\\com\\toly1994\\tolyservice\\IMusicPlayerService.aidl
 */
package com.toly1994.tolyservice;
// Declare any non-default types here with import statements
//在這裡使用import語句宣告任何非預設型別

public interface IMusicPlayerService extends android.os.IInterface {
    //Stub類,暫略...
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
      說明一些在AIDL中可以用作引數和返回值的基本型別。
     */
    public void stop() throws android.os.RemoteException;
    public void pause() throws android.os.RemoteException;
    public void start() throws android.os.RemoteException;
    public void prev() throws android.os.RemoteException;
    public void next() throws android.os.RemoteException;
    public void release() throws android.os.RemoteException;
    public boolean isPlaying() throws android.os.RemoteException;
    public void seek(int pre_100) throws android.os.RemoteException;
    public void create(java.util.List<java.lang.String> filePaths) throws android.os.RemoteException;
}
複製程式碼

4.然後當然是深入一下,到Stub

英語Stub有存根的意思,看完本篇之後自己意會一下它的含義
它不僅繼承Binder,而且實現了IMusicPlayerService,看起來挺牛的,可惜是個抽象類
它實現了Binder的一些東西,但並沒有IMusicPlayerService的任何方法,就是掛個頭而已
實現從成員變數來看,每個方法都對應了一個int常量,從switch來看應該是方法的標識
DESCRIPTOR對應了一個包名.IMusicPlayerService字串,用於attachInterface

Stub分析.png

public static abstract class Stub extends android.os.Binder implements com.toly1994.tolyservice.IMusicPlayerService {
     private static final java.lang.String DESCRIPTOR = "com.toly1994.tolyservice.IMusicPlayerService";

     /**
      * Construct the stub at attach it to the interface.
        在將關聯到介面時構造stub。----注意使用了DESCRIPTOR字串
      */
     public Stub() {
         this.attachInterface(this, DESCRIPTOR);
     }

     /**
      * Cast an IBinder object into an com.toly1994.tolyservice.IMusicPlayerService interface,
      * generating a proxy if needed.
      */
     public static com.toly1994.tolyservice.IMusicPlayerService asInterface(android.os.IBinder obj) {
         if ((obj == null)) {
             return null;
         }
         android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
         if (((iin != null) && (iin instanceof com.toly1994.tolyservice.IMusicPlayerService))) {
             return ((com.toly1994.tolyservice.IMusicPlayerService) iin);
         }
         return new com.toly1994.tolyservice.IMusicPlayerService.Stub.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 {
         java.lang.String descriptor = DESCRIPTOR;
         switch (code) {
             case INTERFACE_TRANSACTION: {
                 reply.writeString(descriptor);
                 return true;
             }
             case TRANSACTION_stop: {//暫略...
                 return true;
             }
             case TRANSACTION_pause: {//暫略...
                 return true;
             }
             case TRANSACTION_start: {//暫略...
                 return true;
             }
             case TRANSACTION_prev: {//暫略...
                 return true;
             }
             case TRANSACTION_next: {//暫略...
                 return true;
             }
             case TRANSACTION_release: {//暫略...
                 return true;
             }
             case TRANSACTION_isPlaying: {//暫略...
                 return true;
             }
             case TRANSACTION_seek: {//暫略...
                 return true;
             }
             case TRANSACTION_create: {//暫略...
                 return true;
             }
             default: {
                 return super.onTransact(code, data, reply, flags);
             }
         }
     }
     
     //Proxy類,暫略...
     static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
     static final int TRANSACTION_pause = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
     static final int TRANSACTION_start = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
     static final int TRANSACTION_prev = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
     static final int TRANSACTION_next = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
     static final int TRANSACTION_release = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
     static final int TRANSACTION_isPlaying = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
     static final int TRANSACTION_seek = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
     static final int TRANSACTION_create = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
 }
複製程式碼

5.現在看最後一層奶油夾心:Proxy

現在把聚光燈打到Stub的內部類Proxy上,Proxy是個敲程式碼的都知道是代理的意思
既然是代理,它代理什麼?成員變數有個IBinder mRemote,構造方法要傳一個IBinder,so...
它實現了IMusicPlayerService,並且有所動作,而且動作基本一致

Proxy.png

 private static class Proxy implements com.toly1994.tolyservice.IMusicPlayerService {
    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;
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    @Override
    public void stop() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public void pause() throws android.os.RemoteException {//暫略...
    }

    @Override
    public void start() throws android.os.RemoteException {//暫略...
    }

    @Override
    public void prev() throws android.os.RemoteException {//暫略...
    }

    @Override
    public void next() throws android.os.RemoteException {//暫略...
    }

    @Override
    public void release() throws android.os.RemoteException {//暫略...
    }

    @Override
    public boolean isPlaying() throws android.os.RemoteException {//暫略...
    }

    @Override
    public void seek(int pre_100) throws android.os.RemoteException {//暫略...
    }

    @Override
    public void create(java.util.List<java.lang.String> filePaths) throws android.os.RemoteException {//暫略...
    }
}
複製程式碼

到現在為止,它們幾個的關係理清了,下面就來詳細看看哪些//暫略...的方法


二、Binder和IBinder的個別方法

這裡先列舉出上面使用到的方法

binder與Ibinder.png


1.目前IBinder檯面上出現的方法
public interface IBinder {
    /**
     * Attempt to retrieve a local implementation of an interface for this Binder object.  
     If null is returned, you will need to instantiate a proxy class to marshall calls through the transact() method.
     嘗試檢索此繫結器物件的本地介面實現。
     如果返回null,則需要通過transact()方法例項化一個代理類來呼叫。
     */
    public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);

    /**
     * Perform a generic operation with the object.
     * 使用物件執行一般操作。
     * @param code The action to perform.  This should be a number between {@link #FIRST_CALL_TRANSACTION} and {@link #LAST_CALL_TRANSACTION}.
    code 操作的行為碼  0x00000001 ~ 0x00ffffff 之間 1~16777215
     * @param data Marshalled data to send to the target.  Must not be null.If you are not sending any data, you must create an empty Parcel that is given here.
    data 傳送到目標的編組資料。不能為空。如果您沒有傳送任何資料,則必須建立這裡給出的空包。
     * @param reply Marshalled data to be received from the target.  May be null if you are not interested in the return value.
     從目標接收到的編組資料。如果您對返回值不感興趣,則可能為null。
     * @param flags Additional operation flags.  Either 0 for a normal RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
   flags  額外的操作標誌,對於普通RPC為0,對於單向RPC為1{@link #FLAG_ONEWAY}。
    
     * @return Returns the result from {@link Binder#onTransact}.  A successful call generally returns true; false generally means the transaction code was not understood.
   return  從Binder.onTransact()返回結果。成功呼叫通常返回true;false通常表示事務程式碼未被理解。
     */
    public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
        throws RemoteException;
    //其他...略
}
複製程式碼

2.目前Binder檯面上出現的方法
---->[Binder]-------------
private IInterface mOwner;
private String mDescriptor;

---->[Binder#attachInterface]-------------
 Convenience method for associating a specific interface with the Binder. 
 After calling, queryLocalInterface() will be implemented for you to return
 the given owner IInterface when the corresponding descriptor is requested.
|--譯:一個將特定介面與Binder關聯的方便方法。呼叫後,將為你實現queryLocalInterface(),
|--以便在請求相應的描述符時返回owner (IInterface物件)。
public void attachInterface(@Nullable IInterface owner, @Nullable Stringdescriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}

---->[Binder#queryLocalInterface]-------------
 Use information supplied to attachInterface() to return the associated 
 IInterface if it matches the requested descriptor.
 如果提供的描述符,與通過attachInterface()方法進行關聯的IInterface一致
 返回該IInterface,否則返回null
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

---->[Binder#onTransact]-------------
 * Default implementation is a stub that returns false.  You will want
 * to override this to do the appropriate unmarshalling of transactions.
 * 預設實現是stub返回false。您會想要覆謝它,以便對事務進行適當的反編組。
 * <p>If you want to call this, call transact().
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel
reply,int flags) throws RemoteException {
複製程式碼

3.再來看剛才暫略的方法
3.1:attachInterface

此方法在Stub的建構函式中呼叫,為成員變數mOwnermDescriptor賦值

attachInterface.png

---->[IMusicPlayerService.Stub#Stub]-------------
public Stub() {
    this.attachInterface(this, DESCRIPTOR);
}
|---這裡呼叫了attachInterface方法,引數是自己和描述符

---->[Binder]-------------
private IInterface mOwner;
private String mDescriptor;

---->[Binder#attachInterface]-------------
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}
複製程式碼

3.2:asInterface

傳入一個IBinder物件obj,呼叫obj的queryLocalInterface方法生成IInterface物件
再進行強轉,如果強轉不成功,才會建立Proxy代理物件

asInterface.png

---->[IMusicPlayerService.Stub#asInterface]-------------
public static com.toly1994.tolyservice.IMusicPlayerService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.toly1994.tolyservice.IMusicPlayerService))) {
        return ((com.toly1994.tolyservice.IMusicPlayerService) iin);
    }
    return new com.toly1994.tolyservice.IMusicPlayerService.Stub.Proxy(obj);
}
複製程式碼

3.3: asBinder

asBinder是IInterface介面的方法,把自己(Stub繼承自Binder)返回去,沒什麼好說的

---->[IMusicPlayerService.Stub#asInterface]-------------
@Override
public android.os.IBinder asBinder() {
    return this;
}
複製程式碼

3.4: onTransact

此方法執行在服務端Binder執行緒池中,客戶端發起跨程式請求時,遠端請求通過系統底層封裝後交由此方法處理。
處理的邏輯基本上都一樣,通過判斷code來呼叫方法,這裡看兩個startseek,

onTransact.png

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel
reply, int flags) throws android.os.RemoteException {
    java.lang.String descriptor = DESCRIPTOR;
    switch (code) {
    //略...
        case TRANSACTION_start: {
            data.enforceInterface(descriptor);//執行介面
            this.start();//呼叫start方法
            reply.writeNoException();//將計算結果寫入reply中
            return true;
        }
        case TRANSACTION_seek: {
            data.enforceInterface(descriptor);//執行介面
            int _arg0;
            _arg0 = data.readInt();//獲取資料
            this.seek(_arg0);//呼叫seek方法
            reply.writeNoException();//將計算結果寫入reply中
            return true;
        }
  //略...
複製程式碼

3.5:Proxy中的實現方法

Proxy是代理類,執行在客戶端,也就是Stub#asInterface中iin
無法強轉成IMusicPlayerService是返回Proxy類物件

---->[IMusicPlayerService.Stub.Proxy#start]--------
@Override
public void start() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
         //向_data中寫入引數
        _data.writeInterfaceToken(DESCRIPTOR);
        //通過transact方法向服務端傳遞引數,並寫入_reply
        mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

@Override
public void seek(int pre_100) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeInt(pre_100);
        mRemote.transact(Stub.TRANSACTION_seek, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}
複製程式碼

4.現在再來看我們在Service中畫的最後一張圖

ServiceConnection的onServiceConnected方法會回撥一個IBinder物件service
並通過IMusicPlayerService.Stub.asInterface方法將service變成IMusicPlayerService物件
還記得asInterface裡做了什麼嗎?下面再看一遍,aidl在使用層面的邏輯是不是清晰了一些?

這裡要強調一下:
圖中:藍色區域可在任意app中使用,相當於客戶端:只要連線就行了   
其他區域是服務端的邏輯,為客戶端提供業務邏輯處理 
複製程式碼

aidl繫結服務.png

asInterface.png

至於Binder在底層怎麼跑的,那是後話,那根鑽石級的硬骨頭還是最後啃吧


5.小結一下:現在回頭再看
1.在一個aidl的服務中,業務邏輯的處理是在哪的?
|--- 這還用問嗎,當然在MusicPlayerStub裡面,不然還能在哪?

2.IMusicPlayerService和Stub有什麼用
|--- IMusicPlayerService是可呼叫的介面
|--- Stub在臺面上有一個功勞,用asInterface將IBinder物件轉化為IMusicPlayerService物件

3.Proxy有什麼用?
|---Proxy唯一出現的是在asInterface方法中,在inn無法強轉成IMusicPlayerService時使用Proxy  

4.什麼時候inn無法轉成IMusicPlayerService?(見下圖)
|--- 我做了一個實驗,就是分別看一下客戶端和服務端繫結時回撥的IBinder物件型別
再呼叫queryLocalInterface方法得到inn,看一下它的型別
服務端是:MusicPlayerStub ,它何許人也? Stub之子,Stub實現了IMusicPlayerService,強轉ok  
客戶端是:BinderProxy物件queryLocalInterface後為null,使用Proxy  
複製程式碼

客戶端與服務端繫結服務的區別.png

queryLocalInterface.png


三、ActivityManagerService初探

1.對於系統級跨程式通訊的Service如何分析
當我們研究原始碼時遇到這種程式碼應該怎麼分析?(如ActivityManagerNative)
可以看出無論Stub和Proxy或Binder對程式碼的邏輯性的分析並沒有太大的用處,
他們只是實現類跨程式通訊的機制,這種機制和邏輯相關性不是非常大

IXXXService只是規定介面方法,需要了解一下,著重的業務邏輯全在XXXXStub的實現類當中,
一般XXXXStub是作為XXXXService的內部類,也就是說看到有關aidl的系統服務原始碼時,
直接分析XXXXService就行了,邏輯全在那裡
複製程式碼

2.AIDL為何而生
|--- 一個字:懶...   
自動生成的那個類,自己敲出來也能跑,既然能自動生成,那就生成唄,AIDL就出來了
這樣就簡化了跨程式間通訊,但是凡事都有兩面性,一旦簡化了,就很難玩花樣
Binder可以很複雜,IBinder還有很多控制的方法,所以AIDL只是最簡單的跨程式間通訊機制  
就像快速生產的板凳和精心雕刻的板凳,雖然都能坐,但價值是天差地別的
複製程式碼

3.ActivityManagerNative分析

還記得第一篇中Activity啟動時出場的ActivityManagerNative嗎?
這就是傳說中的ActivityManagerNative你可以和最上面的IMusicPlayerService對比一下

ActivityManagerNative分析.png


4.ActivityManagerNative的幕後大佬

一般來說都是呼叫ActivityManagerNative.getDefault()生成一個IActivityManager物件
然後呼叫IActivityManager的相應抽象方法,那具體的實現類是誰?
ActivityManagerNative的作用相當於上面的Stub,需要找到一個繼承他的類
這便是ActivityManagerService,注意它並不是Service類物件,而是服務的核心邏輯
它的價值也就相當於我們上面的MusicPlayerStub,不是Service,卻更似服務

ActivityManagerService.png


5.ActivityManagerNative.getDefault()方法說了什麼?

返回一個IActivityManager物件,實際型別為ActivityManagerService

ActivityManagerNative.getDefalut.png

---->[ActivityManagerNative#getDefault]--------------------
static public IActivityManager物件 getDefault() {
    return gDefault.get();
}

//|--這裡通過gDefault成員變數的get()方法獲取了一個IActivityManager物件
|----現在焦點在gDefault身上,來看一下他是什麼

---->[ActivityManagerNativegetDefault.gDefault]--------------------
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        //通過ServiceManager獲取activity的IBinder物件
        IBinder b = ServiceManager獲取.getService("activity");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        //asInterface方法使用IBinder物件,獲取IActivityManager物件
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};

//|--gDefault的資料型別是Singleton<IActivityManager>,有點意思,看一下Singleton類
----單例輔助類啊!create()方法用於穿件物件,get方法獲取單例物件,但它是@hide的,我們不能用
public abstract class Singleton<T> {
    private T mInstance;
    protected abstract T create();
    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

---->[ActivityManagerNativegetDefault.asInterface]--------------------
static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    //如果obj(即Bundle) queryLocalInterface 不為空,就使用in
    IActivityManager in =
        (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }
    //否則返回ActivityManagerProxy物件
    return new ActivityManagerProxy(obj);
}

---->[ActivityManagerNativegetDefault$ActivityManagerProxy]--------------------
class ActivityManagerProxy implements IActivityManager{
    public ActivityManagerProxy(IBinder remote){
        mRemote = remote;
    }
    public IBinder asBinder(){
        return mRemote;
    }
    //略...
        public int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String[] requiredPermissions, int appOp, Bundle options, boolean serialized,
            boolean sticky, int userId) throws RemoteException
    //略...
    }
複製程式碼

這樣使用ActivityManagerNative.getDefault()便可以獲得一個IActivityManager物件,
該物件的實現型別為ActivityManagerService,也就是傳說中的AMS,
所以在看原始碼時ActivityManagerNative.getDefault(),就相當於看到了AMS

相關文章