天天看點

Android 深入淺出AIDL(二)

*本篇文章已授權微信公衆号 guolin_blog (郭霖)獨家釋出

文章目錄

    • 前言
    • 概要
    • 解剖
      • asInterface
      • asBinder
      • IInterface
      • DESCRIPTOR
      • onTransact(服務端接收)
      • transact(用戶端調用)
    • 總結

前言

繼上一篇AIDL的簡單介紹,相信應該對AIDL有一個大緻的了解,那麼這一篇我們來深入探讨一下AIDL為什麼能夠完成這個跨程序操作,這其中是否隐藏着一些不為人知的秘密,讓我們跟着筆者的思路,慢慢撥開籠罩在AIDL上的謎團。

概要

Android 深入淺出AIDL(二)

先用上圖整體描述這個AIDL從用戶端(Client)發起請求至服務端(Server)相應的工作流程,我們可以看出整體的核心就是Binder

解剖

asInterface

用于将服務端的Binder對象轉換成用戶端所需的AIDL接口類型的對象,這種轉換過程是區分程序的【如果用戶端和服務端位于同一程序,那麼此方法傳回的就是服務端的Stub對象本身,否則傳回的是系統封裝後的Stub.proxy對象】
private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            IDemandManager demandManager = IDemandManager.Stub.asInterface(service);
        }
    };

           

ServiceConnection

綁定服務的方法裡,我們通過

IDemandManager.Stub.asInterface(service)

方法獲得

IDemandManager

對象,我們跟着

asInterface

步伐看看裡面有什麼名堂。

PS :

onServiceConnected

方法的(IBinder)service參數在ActivityThread建立,他們之間還會扯出ActivityManagerService,ManagerService等,有興趣的可以通過Activity 的啟動過程加深功力,深入了解。

Android 深入淺出AIDL(二)

從上圖的類結構圖中我們可以看出,這個IDemandManager.aidl檔案通過編譯成為一個接口類,而這個類最核心的成員是Stub類和Stub的内部代理類Proxy。

順着

asInterface

方法,結合上面對該方法的描述,可以看出通過

DESCRIPTOR

辨別判斷

如果是同一程序,那麼就傳回Stub對象本身(

obj.queryLocalInterface(DESCRIPTOR)

),否則如果是跨程序則傳回Stub的代理内部類Proxy。

也就是說這個

asInterface

方法傳回的是一個遠端接口具備的能力(有什麼方法可以調用),在我們項目裡,

asInterface

的能力就是get/setDemand和注冊/解綁監聽接口。

asBinder

緊接着

asInterface

,我們看到一個簡潔的方法

asBinder

顧名思義,asBinder用于傳回目前Binder對象。
//Stub
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
           
//Proxy
        private android.os.IBinder mRemote;

        @Override
        public android.os.IBinder asBinder() {
            return mRemote;
        }
           

根據代碼我們可以追溯到,proxy的這個mRemote就是綁定服務(bindService)時候

IDemandManager.Stub.asInterface(binder);

傳入的

IBinder

對象。

這個Binder對象具有跨程序能力,在Stub類裡面(也就是本程序)直接就是Binder本地對象,在Proxy類裡面傳回的是遠端代理對象(Binder代理對象)。[是以跨程序的謎團,會随着對Binder的分析和研究,逐漸變得清晰起來。]

因為我們這個編譯生成的

IDemandManager

接口繼承了

android.os.IInterface

接口,是以我們先分析

IInterface

接口。

IInterface

而這個

IInterface

接口就隻聲明了一個方法,但是Stub和Proxy都分别間接的實作了該接口。

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}
           

Binder接口的基類。 定義新接口時,

你必須實作IInterface接口。

檢索與此界面關聯的Binder對象。

你必須使用它而不是一個簡單的轉換

這樣代理對象才可以傳回正确的結果。

從上面的系統注釋中我們可以了解出:

  1. 要聲明(或者是手動diy建立)AIDL性質的接口,就要繼承

    IInterface

  2. 代表遠端server對象具有的能力,具體是由

    Binder

    表達出這個能力。

DESCRIPTOR

Binder的唯一辨別,一般用目前Binder的類名表示。

onTransact(服務端接收)

我們發現

IDemandManager

接口,實際上并沒有太多複雜的方法,看完了

asInterface

asBinder

方法,我們再來分析

onTransact

方法。

onTransact

方法運作在服務端中的Binder線程池中

用戶端發起跨程序請求時,遠端請求會通過系統底層封裝後交給此方法來處理。

如果此方法傳回false,那麼用戶端的請求就會失敗。

  • code : 确定用戶端請求的目标方法是什麼。(項目中的getDemand或者是setDemandIn方法)
  • data : 如果目标方法有參數的話,就從data取出目标方法所需的參數。
  • reply : 當目标方法執行完畢後,如果目标方法有傳回值,就向reply中寫入傳回值。
  • flag : Additional operation flags. Either 0 for a normal RPC, or FLAG_ONEWAY for a one-way RPC.(暫時還沒有發現用處,先标記上英文注釋)
Android 深入淺出AIDL(二)

也就是說,這個

onTransact

方法就是服務端處理的核心,接收到用戶端的請求,并且通過用戶端攜帶的參數,執行完服務端的方法,傳回結果。下面通過系統生成的代碼,我們簡要的分析一下

onTransact

方法裡我們項目寫的

setDemandIn

setDemandOut

方法。

case TRANSACTION_setDemandIn: {
                    data.enforceInterface(DESCRIPTOR);
                    qdx.aidlserver.MessageBean _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = qdx.aidlserver.MessageBean.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.setDemandIn(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_setDemandOut: {
                    data.enforceInterface(DESCRIPTOR);
                    qdx.aidlserver.MessageBean _arg0;
                    _arg0 = new qdx.aidlserver.MessageBean();
                    this.setDemandOut(_arg0);
                    reply.writeNoException();
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply,android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
           

此段代碼并沒有多少玄機,就是負責資料的讀寫,以及結果的傳回。

但是有一個知識點可以在此驗證,就是定向tag的作用,上面的方法定向tag分别是in和out,上篇文章介紹定向tag的作用就是跨程序中資料的流向,

setDemandIn

方法中我們可以看到我們讀取了用戶端傳遞過來的資料參數data,即用戶端->服務端。out方法可以同理自行分析。AIDL中的in,out,inout分析

transact(用戶端調用)

分析完了Stub,就剩下Stub的内部代理類Proxy

驚奇的發現Proxy類主要是用來方法調用,也就是用來用戶端的跨程序調用。

Android 深入淺出AIDL(二)

transact

方法運作在用戶端,首先它建立該方法所需要的輸入型Parcel對象_data、輸出型Parcel對象_reply;

接着調用綁定服務傳來的IBinder對象的transact方法來發起遠端過程調用(RPC)請求,同時目前線程挂起;

然後服務端的

onTransact

方法會被調用,直到RPC過程傳回後,目前線程繼續執行,并從_reply中取出RPC過程的傳回結果,也就是傳回_reply中的資料。

我們看到獲得

Parcel

的方法為

Parcel.obtain()

,按照套路這個應該就是從

Parcel

池中擷取該對象,減少建立對象的開支,跟進方法我們可以看得的确是建立了一個

POOL_SIZE

為6的池用來擷取

Parcel

對象。

是以在跨程序通訊中Parcel是通訊的基本單元,傳遞載體。

而這個

transact

方法是一個本地方法,在native層中實作,功力不足,點到為止。

分析到了這裡,感覺頓悟了許多,再一次引用開頭概述的圖檔來看大概,整體的思路便浮在腦海中。so 哒死内~

Android 深入淺出AIDL(二)

總結

通過對AIDL的分析,我們發現原來這一切圍繞着Binder有序的展開

AIDL通過Stub類用來接收并處理資料,Proxy代理類用來發送資料,而這兩個類也隻是通過對Binder的處理和調用,下一篇我們将深入摸清這個Binder究竟為何物,能夠輕松遊走于跨程序之中。

Android 深入淺出AIDL(二)

是以所謂的服務端和用戶端,我們拆開看之後發現原來竟是不同類的處理

Stub充當服務端角色,持有Binder實體(本地對象)。

  • 擷取用戶端傳過來的資料,根據方法 ID 執行相應操作。
  • 将傳過來的資料取出來,調用本地寫好的對應方法。
  • 将需要回傳的資料寫入 reply 流,傳回用戶端。

Proxy代理類充當用戶端角色,持有Binder引用(句柄)。

  • 生成 _data 和 _reply 資料流,并向 _data 中存入用戶端的資料。
  • 通過 transact() 方法将它們傳遞給服務端,并請求服務端調用指定方法。
  • 接收 _reply 資料流,并從中取出服務端傳回來的資料。

而且所謂的服務端和用戶端都是相對而言的,服務端不僅可以接收和處理消息,而且可以定時往用戶端發送資料,與此同時服務端使用Proxy類跨程序調用,相當于充當了"Client"。

并且有一點要了解是,跨程序通訊的時候,傳遞的資料對象并不是從程序A原原本本的發給程序B。庫克說要送你蘋果,而你收到的iphone X就真的是美國生産的嗎?不,它也可能是made in China.

終上所述,AIDl這個工具就已經分析結束,如果文中有不足之處還望指出。如果有什麼更好的見解也可以留言,最終的目标都是共同進步。有興趣的可以繼續看Android Binder之應用層總結與分析。

AIDL文章借鑒(上)

AIDL文章借鑒(下)

最後感謝《Android 開發藝術探索》專業的分析。