Android 跨進程通訊 深刻淺出AIDL(二)

 

前言

上一篇AIDL的簡單介紹,相信應該對AIDL有一個大體的瞭解,那麼這一篇咱們來深刻探討一下AIDL爲何可以完成這個跨進程操做,這其中是否隱藏着一些鮮爲人知的祕密,讓咱們跟着筆者的思路,慢慢撥開籠罩在AIDL上的謎團。java

 

概要

 


這裏寫圖片描述
先用上圖總體描述這個AIDL從客戶端(Client)發起請求至服務端(Server)相應的工做流程,咱們能夠看出總體的核心就是Binderandroid

 

 

解剖

asInterface

用於將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象,這種轉換過程是區分進程的【若是客戶端和服務端位於同一進程,那麼此方法返回的就是服務端的Stub對象自己,不然返回的是系統封裝後的Stub.proxy對象】iphone

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 的啓動過程加深功力,深刻了解。ide

 


這裏寫圖片描述

 

從上圖的類結構圖中咱們能夠看出,這個IDemandManager.aidl文件經過編譯成爲一個接口類,而這個類最核心的成員是Stub類和Stub的內部代理類Proxy。 
順着asInterface方法,結合上面對該方法的描述,能夠看出經過DESCRIPTOR標識判斷 
若是是同一進程,那麼就返回Stub對象自己(obj.queryLocalInterface(DESCRIPTOR)),不然若是是跨進程則返回Stub的代理內部類Proxy。 
也就是說這個asInterface方法返回的是一個遠程接口具有的能力(有什麼方法能夠調用),在咱們項目裏,asInterface的能力就是get/setDemand和註冊/解綁監聽接口。工具

 

asBinder

緊接着asInterface,咱們看到一個簡潔的方法asBinderthis

顧名思義,asBinder用於返回當前Binder對象。spa

//Stub
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
//Proxy
        private android.os.IBinder mRemote;

        @Override
        public android.os.IBinder asBinder() {
            return mRemote;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

根據代碼咱們能夠追溯到,proxy的這個mRemote就是綁定服務(bindService)時候 
IDemandManager.Stub.asInterface(binder);傳入的IBinder對象。.net

這個Binder對象具備跨進程能力,在Stub類裏面(也就是本進程)直接就是Binder本地對象,在Proxy類裏面返回的是遠程代理對象(Binder代理對象)。[因此跨進程的謎團,會隨着對Binder的分析和研究,逐漸變得清晰起來。]線程

由於咱們這個編譯生成的IDemandManager接口繼承了android.os.IInterface接口,因此咱們先分析IInterface接口。代理

public interface IDemandManager extends android.os.IInterface
  • 1

 

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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Binder接口的基類。 定義新接口時, 
你必須實現IInterface接口。

檢索與此界面關聯的Binder對象。 
你必須使用它而不是一個簡單的轉換 
這樣代理對象才能夠返回正確的結果。

從上面的系統註釋中咱們能夠理解出:

  1. 要聲明(或者是手動diy建立)AIDL性質的接口,就要繼承IInterface
  2. 表明遠程server對象具備的能力,具體是由Binder表達出這個能力。

 

DESCRIPTOR

private static final java.lang.String DESCRIPTOR = "qdx.aidlserver.IDemandManager";//系統生成的
  • 1

Binder的惟一標識,通常用當前Binder的類名錶示。

 

onTransact(服務端接收)

咱們發現IDemandManager接口,實際上並無太多複雜的方法,看完了asInterfaceasBinder方法,咱們再來分析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.(暫時尚未發現用處,先標記上英文註釋)

 


這裏寫圖片描述

 

也就是說,這個onTransact方法就是服務端處理的核心,接收到客戶端的請求,而且經過客戶端攜帶的參數,執行完服務端的方法,返回結果。下面經過系統生成的代碼,咱們簡要的分析一下onTransact方法裏咱們項目寫的setDemandInsetDemandOut方法。

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;
                }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

此段代碼並無多少玄機,就是負責數據的讀寫,以及結果的返回。 
可是有一個知識點能夠在此驗證,就是定向tag的做用,上面的方法定向tag分別是in和out,上篇文章介紹定向tag的做用就是跨進程中數據的流向,setDemandIn方法中咱們能夠看到咱們讀取了客戶端傳遞過來的數據參數data,即客戶端->服務端。out方法能夠同理自行分析。AIDL中的in,out,inout分析

 

transact(客戶端調用)

分析完了Stub,就剩下Stub的內部代理類Proxy 
驚奇的發現Proxy類主要是用來方法調用,也就是用來客戶端的跨進程調用。

 


這裏寫圖片描述

 

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 噠死內~

 


這裏寫圖片描述

 



 

總結

經過對AIDL的分析,咱們發現原來這一切圍繞着Binder有序的展開 
AIDL經過Stub類用來接收並處理數據,Proxy代理類用來發送數據,而這兩個類也只是經過對Binder的處理和調用,下一篇咱們將深刻摸清這個Binder究竟爲什麼物,可以輕鬆遊走於跨進程之中。

 


這裏寫圖片描述

 

因此所謂的服務端和客戶端,咱們拆開看以後發現原來竟是不一樣類的處理

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 開發藝術探索》專業的分析。

相關文章
相關標籤/搜索