歡迎光臨
每天分享高質量文章

關於Binder,作為應用開發者你需要知道的全部

作者:rushjs

來自:https://www.jianshu.com/p/062a6e4f5cbe


github 地址:https://github.com/rushgit/zhongwenjun.github.com

csdn:https://blog.csdn.net/zwjemperor

為什麼要理解Binder?

一般Android應用開發很少直接用到跨行程信通信(IPC),但如果你想知道:

  • App是如何啟動並初始化的?

  • Activity的啟動過程是怎樣的?

  • 行程間是如何通信的?

  • AIDL的具體原理是什麼?

  • 眾多插件化框架的設計原理 等等

就必須對Binder有所瞭解,無論是四大組件,還是各種系統Service,比如ActivityManagerService、PackageManagerService,它們的實現都依賴Binder的通信機制,可見Binder在Android系統中的重要性,可以說Binder是邁入高級工程師的第一步。

Binder機制很複雜,想要徹底弄懂比較難,除了需要瞭解操作系統中的各種知識外,還需要看懂Binder驅動層的代碼實現。最近看了很多關於Binder的文章,大部分過於抽象或者過於深入原始碼細節,真正淺顯易懂的文章很少。個人認為最有參考價值的是以下三篇:(其他參考文章附在文末)

  • Android Binder設計與實現 – 設計篇

  • Binder學習指南

  • 寫給 Android 應用工程師的 Binder 原理剖析

這篇文章主要從宏觀的層面去理解Binder中的各種概念和基本通信過程,只關註Java層的實現,底層實現不做介紹。對於應用開發者而言,理解Binder的基本設計原理和通信過程已經夠了,想要深入理解Binder需要自行閱讀原始碼。

本文主要從三個方面來做分析:

1、為什麼是Binder?

  • 傳統Linux IPC機制的缺點

  • Linux的一些基本知識

  • 傳統Linux IPC機制的通信原理


2、Binder的基本原理

  • Binder的底層原理

  • Binder的通信模型

  • Binder的代理機制

  • 對Binder概念的重新理解


3、通過代碼來理解Binder

  • 通過AIDL實體來瞭解Binder的用法

  • 通過手動編碼實現ActivityManagerService


1、為什麼是Binder?

1.1 傳統IPC機制的缺點

大家都知道Android系統是基於Linux內核實現的,Linux已經提供了多種行程間通信機制,比如:管道、訊息佇列、共享記憶體和套接字(Socket)等等,為什麼還要再實現一套IPC機制呢?主要是基於兩方面的原因:

1.1.1 性能角度

管道、訊息佇列、Socket實現一次行程通信都需要2次記憶體拷貝,效率太低;共享記憶體雖然不需要拷貝記憶體,但管理複雜;Binder只需要一次記憶體拷貝,從性能角度來看,低於共享記憶體方式,優於其它方式。

IPC機制 資料拷貝次數
共享記憶體 0
Binder 1
管道、訊息佇列、Socket 2
1.1.2 安全性考慮

傳統的IPC機制沒有安全措施,接收方無法獲得對方可靠的行程ID或用戶ID,完全靠上層的協議來保護,比如Socket通信的IP地址是客戶端填入的,很可能被惡意程式篡改。Android作為面向終端用戶的開源平臺,應用市場中有海量的應用供用戶選擇,因此安全性極為重要。Android系統為每個已安裝的App都分配了用戶ID(UID),UID是鑒別行程身份的重要標識,通過UID可以進行一系列的權限校驗。另一方面 ,傳統IPC的接入點是開放的,任何程式都可以根據協議進行訪問,無法阻止惡意程式的訪問,Android需要一種基於C/S架構的IPC機制,Server端需要能夠對Client的請求進行身份校驗,來保證資料的安全性。

1.2 Linux的一些基本知識

要知道Binder是如何只用一次記憶體拷貝即實現跨行程通信的,首先需要弄清楚為什麼傳統IPC機製為什麼需要兩次記憶體拷貝,這就需要先瞭解一些操作系統的基礎知識。

1.2.1 行程隔離

先來看一下維基百科對“行程隔離”的定義:

行程隔離是為保護操作系統中行程互不干擾而設計的一組不同硬體和軟體的技術。這個技術是為了避免行程A寫入行程B的情況發生。 行程的隔離實現,使用了虛擬地址空間。行程A的虛擬地址和行程B的虛擬地址不同,這樣就防止行程A將資料信息寫入行程B。

也就是說,行程之間的資料是不共享的,A行程無法直接訪問B行程的資料,以此來保證資料的安全性。在行程隔離的操作系統中,行程之間的交互必須通過IPC機制。

行程隔離的實現使用了虛擬地址空間,什麼是虛擬地址空間呢?首先需要瞭解操作系統中的虛擬記憶體概念,它是一種提高編程效率和提高物理記憶體利用效率的一種技術。簡單來說,就是應用程式看到了都一片連續完整的記憶體地址空間,而實際上這些地壇空間是映射到碎片化的物理記憶體中的,這個映射的過程對應用程式來說是透明的。這個概念很重要,對於虛擬記憶體更深入的理解可以參考這篇文章:Linux 虛擬記憶體和物理記憶體的理解

1.2.2 行程空間:用戶空間/內核空間

現在的操作系統都採用虛擬記憶體,對32位的操作系統而言,尋址空間是2的32次方,即4G。操作系統的核心是內核,內核擁有對底層設備的所有訪問權限,因此需要和普通的應用程式獨立開來,用戶行程不能直接訪問內核行程。操作系統從邏輯上把虛擬地址空間劃分為用戶空間(User Space)和內核空間(Kernel Space)。在32位的Linux操作系統中,將高位的1GB位元組供內核使用,稱之為內核空間;剩下的3GB位元組供用戶行程使用,稱之為用戶空間。

1.2.3 系統呼叫:用戶態/內核態

因為用戶空間的權限低於內核空間,不可避免用戶空間需要訪問內核空間的資源,比如讀寫檔案和網絡訪問,如何實現呢?唯一的方式就是通過操作系統提供的系統呼叫接口,通過系統呼叫接口,用戶程式可以在內核的控制下實現對內核資源的有限訪問,這樣既能滿足應用程式的資源請求,也能保障系統安全和穩定。

當用戶行程執行自己的代碼時,行程當前就處於用戶運行態(用戶態),此時處理器執行用戶代碼,權限較低;當用戶行程通過系統呼叫執行內核代碼時,行程就暫時進入了內核運行態(內核態),此時處理器權限最高,可以執行特權指令。

1.2.4 內核模塊/驅動

前面說了用戶空間可以通過系統呼叫訪問內核空間,那用戶空間之間(行程間)怎麼通信呢?傳統的IPC機制都是通過內核來支持的,Binder也一樣,有一個運行在內核中的Binder驅動程式負責行程之間Binder的通信。

驅動程式一般指的是設備驅動程式(Device Driver),是一種可以使計算機和設備通信的特殊程式。相當於硬體的接口,操作系統只有通過這個接口。

Binder驅動是一種虛擬的字符設備,註冊在/dev/binder中,其定義了一套Binder通信協議,負責建立行程間的Binder通信,提供了資料包在行程之間傳遞的一系列底層支持。應用行程訪問Binder驅動也是通過系統呼叫實現的。

1.3 傳統IPC機制的通信原理

瞭解了上面的基礎知識後,我們來看看傳統IPC機制是如何實現,通常是下麵兩個步驟(共享記憶體機制除外):

1、發送方行程通過系統呼叫(copy_from_user)將要發送的資料存拷貝到內核快取區中。

2、接收方開闢一段記憶體空間,內核通過系統呼叫(copy_to_user)將內核快取區中的資料拷貝到接收方的記憶體快取區。


這種傳統IPC機制存在2個問題:

1、需要進行2次資料拷貝,第1次是從發送方用戶空間拷貝到內核快取區,第2次是從內核快取區拷貝到接收方用戶空間。

2、接收方行程不知道事先要分配多大的空間來接收資料,可能存在空間上的浪費。

2、Binder的基本原理

2.1 Binder底層原理

傳統IPC機制需要拷貝2次記憶體,Binder是如何只用1次記憶體拷貝就實現行程間通信的呢?前面我們已經瞭解到,Linux是使用的是虛擬記憶體尋址方式,用戶空間的虛擬記憶體地址是映射到物理記憶體中的,對虛擬記憶體的讀寫實際上是對物理記憶體的讀寫,這個過程就是記憶體映射,這個記憶體映射過程是通過系統呼叫mmap()來實現的。

Binder借助了記憶體映射的方法,在內核空間和接收方用戶空間的資料快取區之間做了一層記憶體映射。這樣一來,從發送方用戶空間拷貝到內核空間快取區的資料,就相當於直接拷貝到了接收方用戶空間的資料快取區,從而減少了一次資料拷貝。

2.2 Binder通信模型

Binder是基於C/S架構的,對於通信雙方來說,發起請求的行程屬於Client,接收請求的行程屬於Server,由於存在行程隔離,雙方不能直接通信,Binder是如何實現的呢?

寫給 Android 應用工程師的 Binder 原理剖析中舉的網絡通信例子很貼切,Binder的通信過程與網絡請求類似,網絡通信過程可以簡化為4個角色:Client、Server、DNS服務器和路由器。一次完整的網絡通信大體過程如下:

1、Client輸入Server的域名


2、DNS解析域名

通過域名是無法直接找到相應Server的,必須先通過DNS服務器將Server的域名轉化為具體的IP地址。


3、通過路由器將請求發送至Server

Client通過DNS服務器解析到Server的IP地址後,也還不能直接向Server發起請求,需要經過路由器的層層中轉才還到達Server。


4、Server傳回資料

Server接收到請求並處理後,再通過路由器將資料傳回給Client。

在Binder機制中,也定義了4個角色:Client、Server、Binder驅動和ServiceManager

  • Binder驅動:類似網絡通信中的路由器,負責將Client的請求轉發到具體的Server中執行,並將Server傳回的資料傳回給Client。

  • ServiceManager:類似網絡通信中的DNS服務器,負責將Client請求的Binder描述符轉化為具體的Server地址,以便Binder驅動能夠轉發給具體的Server。Server如需提供Binder服務,需要向ServiceManager註冊。

具體的通信過程是這樣的:

1、Server向ServiceManager註冊

Server通過Binder驅動向ServiceManager註冊,宣告可以對外提供服務。ServiceManager中會保留一份映射表:名字為zhangsan的Server對應的Binder取用是0x12345。


2、Client向ServiceManager請求Server的Binder取用

Client想要請求Server的資料時,需要先通過Binder驅動向ServiceManager請求Server的Binder取用:我要向名字為zhangsan的Server通信,請告訴我Server的Binder取用。


3、向具體的Server發送請求

Client拿到這個Binder取用後,就可以通過Binder驅動和Server進行通信了。


4、Server傳回結果

Server響應請求後,需要再次通過Binder驅動將結果傳回給Client。

可以看到,Client、Server、ServiceManager之間的通信都是通過Binder驅動作為橋梁的,可見Binder驅動的重要性。也許你還有一點疑問,ServiceManager和Binder驅動屬於兩個不同的行程,它們是為Client和Server之間的行程間通信服務的,也就是說Client和Server之間的行程間通信依賴ServiceManager和Binder驅動之間的行程間通信,這就像是:“蛋生雞,雞生蛋,但第一個蛋得通過一隻雞孵出來”。Binder機制是如何創造第一隻下蛋的雞呢?

1、當Android系統啟動後,會創建一個名稱為servicemanager的行程,這個行程通過一個約定的命令BINDERSETCONTEXT_MGR向Binder驅動註冊,申請成為為ServiceManager,Binder驅動會自動為ServiceManager創建一個Binder物體(第一隻下蛋的雞);

2、並且這個Binder物體的取用在所有的Client中都為0,也就說各個Client通過這個0號取用就可以和ServiceManager進行通信。Server通過0號取用向ServiceManager進行註冊,Client通過0號取用就可以獲取到要通信的Server的Binder取用。

Android Binder設計與實現 – 設計篇中對Client、Server、Binder驅動和ServiceManager有更詳細的介紹。

2.3 Binder的代理機制

通過上面的分析,我們已經知道了Binder的基本通信過程:Client向SerivceManger獲取到Server的Binder取用,Client通過Binder取用向Server發起具體請求。Client通過這個Binder取用具體是如何呼叫Server方法的呢?

比如一個Server提供add方法,Client實際請求add的流程是這樣的:Client先通過Binder驅動向ServiceManager獲取Server的Binder取用,這個取用就是一個Java Object,這個Object有一個add方法;Cient拿到這個Object後就可以直接請求add方法了。

實際上Client拿到的Object並不是Server真正的Binder物體,Binder驅動做了一層物件轉換,將這個Object包裝成了一個代理物件ProxyObject,這個ProxyObject和真正的Binder物體有相同的方法簽名,Client通過這個ProxyObject請求add方法時,Binder驅動會自動將請求轉發到具體的Binder物體中執行,這就是Binder的代理機制。由於ProxyObject和真正的Binder物體有相同的方法簽名,其實Client並不需要關心是ProxyObject還是真實的Object。

為了方便描述,下麵將Server真正的Binder物體稱為Binder本地物件;將Client中的Binder取用,即ProxyObject,稱之為Binder代理物件

2.4 對Binder概念的重新理解

經過上面的分析,我們已經大體清楚了Binder機制的基本通信原理,現在回過頭來重新梳理下對Binder機制的認識:

總體來說,Binder是基於C/S結構的一種面向物件的IPC機制。包含:Client、Server、Binder驅動和ServiceManager四大組成部分。各組成部分中的Binder含義都有所有不同:

  • 對於Client

    Binder是Server本地物件的一個取用,這個取用實際上是一個代理物件,Client通過這個代理物件來間接訪問Server的本地物件;

  • 對於Server

    Binder是提供具體實現的本地物件,需向ServiceManager註冊;

  • 對於Binder驅動

    它是連接Client來Server的橋梁,負責將代理物件轉化為本地物件,並將Server的執行結果傳回給Client。

  • 對於ServiceManager

    它儲存了Server Binder字符名稱和Binder取用的映射,Client通過它來找到Server的Binder取用。

Binder驅動中保留了Binder代理物件和Binder本地物件的具體結構,由於我們只關心Binder的基本通信機制,底層實現不做過多介紹,想具體瞭解的同學可以參考Android Binder設計與實現 – 設計篇。

3、通過代碼來理解Binder

上面的介紹比較抽象,現在我們通過具體實體來理解Binder。

1、通過AIDL實體來瞭解Binder的用法

2、通過手動編碼實現ActivityManagerService

3.1 通過AIDL實體來瞭解Binder的用法

實現Binder通信的最常用方法就是通過aidl,aidl接口定義了Client和Server進行通信的接口,對aidl不瞭解的同學請參考官方文件Android 接口定義語言 (AIDL)。

3.1.1 與Binder相關的幾個類的職責

在具體分析之前,我們需要先瞭解與Binder相關的幾個類的職責:

  • IBinder

    跨行程通信的Base接口,它宣告了跨行程通信需要實現的一系列抽象方法,實現了這個接口就說明可以進行跨行程通信,Client和Server都要實現此接口。

  • IInterface

    這也是一個Base接口,用來表示Server提供了哪些能力,是Client和Server通信的協議。

  • Binder

    提供Binder服務的本地物件的基類,它實現了IBinder接口,所有本地物件都要繼承這個類。

  • BinderProxy

    在Binder.java這個檔案中還定義了一個BinderProxy類,這個類表示Binder代理物件它同樣實現了IBinder接口,不過它的很多實現都交由native層處理。Client中拿到的實際上是這個代理物件。

  • Stub

    這個類在編譯aidl檔案後自動生成,它繼承自Binder,表示它是一個Binder本地物件;它是一個抽象類,實現了IInterface接口,表明它的子類需要實現Server將要提供的具體能力(即aidl檔案中宣告的方法)。

  • Proxy

    它實現了IInterface接口,說明它是Binder通信過程的一部分;它實現了aidl中宣告的方法,但最終還是交由其中的mRemote成員來處理,說明它是一個代理物件,mRemote成員實際上就是BinderProxy。

3.1.2 AIDL實體

首先定義一個aidl檔案,這個接口中宣告了一個getPid方法:

// IRemoteService.aidl
package com.rush.demo.aidltest;

interface IRemoteService {
    int getPid();
}

下麵是編譯IRemoteService.aild後生成的java類:

// IRemoteService.java
package com.rush.demo.aidltest;

public interface IRemoteService extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.rush.demo.aidltest.IRemoteService {
        //Binder描述符
        private static final java.lang.String DESCRIPTOR = "com.rush.demo.aidltest.IRemoteService";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.rush.demo.aidltest.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.rush.demo.aidltest.IRemoteService))) {
                return ((com.rush.demo.aidltest.IRemoteService) iin);
            }
            return new com.rush.demo.aidltest.IRemoteService.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getPid: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    int _result = this.getPid(_arg0);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.rush.demo.aidltest.IRemoteService {
            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;
            }

            @Override
            public int getPid(java.lang.String name) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(name);
                    mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

        static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public int getPid(java.lang.String name) throws android.os.RemoteException;
}

這個檔案中有3個類:

1、IRemoteService

繼承至IInterface接口,宣告了IRemoteService.aidl中宣告的getPid方法,它是Client和Service通信的接口。

2、IRemoteService.Stub

IRemoteService的靜態抽象內部類,繼承自Binder,其子類需要實現IRemoteService接口,表明它是Server的Binder本地物件,需要實現getPid接口。

3、IRemoteService.Stub.Proxy

IRemoteService.Stub的靜態內部類,它並沒有繼承自Binder,而是包含了一個IBinder物件,這個物件其實是BinderProxy,說明它是Server在Client中的本地代理物件。Proxy實現了getPid接口,將引數序列化後交由mRemote(BinderProxy)處理,實際上就是交給Binder驅動來完成與遠程Stub的通信。

先來看Stub中的asInterface方法,這個方法通常是Client在bindService成功後,由Client來呼叫的,作用是將系結成功後傳回的IBinder物件轉換為具體的IInterface接口,Client拿到這個IInterface接口後就可以和自由的呼叫Server提供的方法了。

public static com.rush.demo.aidltest.IRemoteService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.rush.demo.aidltest.IRemoteService))) {
        return ((com.rush.demo.aidltest.IRemoteService) iin);
    }
    return new com.rush.demo.aidltest.IRemoteService.Stub.Proxy(obj);
}

asInterface方法中既可能傳回Stub本身的IRemoteService物件,也可能創建一個Proxy物件,這是為什麼呢?因為Binder雖然是跨行程通信機制,但也可以為本行程服務,也就是說Client和Server可能在同一個行程,在同一個行程就沒必要通過Binder驅動來中轉了,直接訪問就可以了;如果Client和Server在不同的行程,就需要通過Binder代理物件來中轉。也就是說:

1、Client和Server在同一個行程,obj是Binder本地物件(Stub的子類),asInterface方法傳回的就是Binder本地物件;

2、Client和Server在不同的行程,obj實際上是Binder代理物件,asInterface傳回一個Proxy物件。


//Binder.java
/**
 * obj.queryLocalInterface是怎樣去查找是否有本地的IInterface呢,從Binder的代碼中可以看到,只是簡單的比較Binder的描述符和要查找的描述符是否匹配,匹配的話直接傳回mOwner,這個mOwner就是Stub構造方法中呼叫attachInterface方法傳入的this引數。
 */

public IInterface queryLocalInterface(String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

final class BinderProxy implements IBinder {
    public IInterface queryLocalInterface(String descriptor) {
        return null;
    }
}

public static abstract class Stub extends android.os.Binder implements com.rush.demo.aidltest.IRemoteService {
    // Binder描述符,值為接口類名全稱
    private static final java.lang.String DESCRIPTOR = "com.rush.demo.aidltest.IRemoteService";

    public Stub() {
        //向Binder中系結owner和descriptor
        this.attachInterface(this, DESCRIPTOR);
    }
}

obj.queryLocalInterface是怎樣去查找是否有本地的IInterface呢,從Binder的代碼中可以看到,只是簡單的比較Binder的描述符和要查找的描述符是否匹配,匹配的話直接傳回mOwner,這個mOwner就是Stub構造方法中呼叫attachInterface方法傳入的this引數。而BinderProxy的queryLocalInterface方法直接傳回null。

Client中通過Binder呼叫Server方法有兩種場景:

1、Client和Server在同一個行程

Stub.asInterface方法傳回的是Stub物件,即Binder本地物件。也就是說和Binder跨行程通信無關,直接呼叫即可,此時Client呼叫方和Server響應方在同一個執行緒中。


2、Client和Server在不同的行程

Stub.asInterface方法傳回的是Binder代理物件,需要通過Binder驅動完成跨行程通信。這種場景下,Client呼叫方執行緒會被掛起(Binder也提供了異步的方式,這裡不討論),等待Server響應後傳回資料。這裡要註意的是,Server的響應是在Server行程的Binder執行緒池中處理的,並不是主執行緒。

接下來分析跨行程場景下,Client呼叫getPid方法的具體流程:

1、Client呼叫Binder代理物件,Client執行緒掛起

Client中拿到的IRemoteService取用實際上是Proxy,呼叫getPid方法實際上是呼叫Proxy的getPid方法,這個方法只是將引數序列化後,呼叫了mRemote成員的transact方法。Stub類中為IRemoteService中的每個方法定義了方法編號,transact方法中傳入getPid方法的編號。此時Client呼叫方執行緒掛起,等待Server響應資料


// Stub.Proxy
public int getPid(java.lang.String name) throws android.os.RemoteException {
    ...
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeString(name);
    mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
    _reply.readException();
    _result = _reply.readInt();
    ...
    return _result;
}


2、Binder代理物件將請求派發給Binder驅動

Proxy中的mRemote成員實際上是BinderProxy,而BinderProxy中的transact方法最終呼叫於transactNative方法,也就是說Client的請求派發給了Binder驅動來處理。


3、Binder驅動將請求派發給Server

Binder驅動經過一系列的處理後,將請求派發給了Server,即呼叫Server本地Binder物件(Stub)的onTransact方法最終在此方法中完成getPid方法的具體呼叫。在onTransact方法中,根據Proxy中呼叫transact時傳入的方法編號來區別具體要處理的方法。


// Stub
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    switch (code) {
        ...
        case TRANSACTION_getPid: {
            data.enforceInterface(DESCRIPTOR);
            //獲取方法引數
            java.lang.String _arg0 = data.readString();
            //呼叫getPid方法,這個方法在Stub的子類,即Server中實現
            int _result = this.getPid(_arg0);
            reply.writeNoException();
            reply.writeInt(_result);
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}


4、喚醒Client執行緒,傳回結果

onTransact處理結束後,將結果寫入reply並傳回至Binder驅動,驅動喚醒掛起的Client執行緒,並將結果傳回。至此,一次跨行程通信完成。

3.2 手動編碼來實現ActivityManagerService

通過前面的示例我們已經知道,aidl檔案只是用來定義C/S交互的接口,Android在編譯時會自動生成相應的Java類,生成的類中包含了Stub和Proxy靜態內部類,用來封裝資料轉換的過程,實際使用時只關心具體的Java接口類即可。為什麼Stub和Proxy是靜態內部類呢?這其實只是為了將三個類放在一個檔案中,提高代碼的聚合性。通過上面的分析,我們其實完全可以不通過aidl,手動編碼來實現Binder的通信,下麵我們通過編碼來實現ActivityManagerService。

首先定義IActivityManager接口:

public interface IActivityManager extends IInterface {
    //binder描述符
    String DESCRIPTOR = "android.app.IActivityManager";
    //方法編號
    int TRANSACTION_startActivity = IBinder.FIRST_CALL_TRANSACTION + 0;
    //宣告一個啟動activity的方法,為了簡化,這裡只傳入intent引數
    int startActivity(Intent intent) throws RemoteException;
}

其次,實現ActivityManagerService側的本地Binder物件基類:

// 名稱隨意,不一致叫Stub
public abstract class ActivityManagerNative extends Binder implements IActivityManager {

    public static IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in = (IActivityManager) obj.queryLocalInterface(IActivityManager.DESCRIPTOR);
        if (in != null) {
            return in;
        }
        //代理物件,見下麵的代碼
        return new ActivityManagerProxy(obj);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            // 獲取binder描述符
            case INTERFACE_TRANSACTION:
                reply.writeString(IActivityManager.DESCRIPTOR);
                return true;
            // 啟動activity,從data中反序列化出intent引數後,直接呼叫子類startActivity方法啟動activity。
            case IActivityManager.TRANSACTION_startActivity:
                data.enforceInterface(IActivityManager.DESCRIPTOR);
                Intent intent = Intent.CREATOR.createFromParcel(data);
                int result = this.startActivity(intent);
                reply.writeNoException();
                reply.writeInt(result);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }
}

再次,實現Client側的代理物件:

public class ActivityManagerProxy implements IActivityManager {
    private IBinder mRemote;

    public ActivityManagerProxy(IBinder remote) {
        mRemote = remote;
    }

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

    @Override
    public int startActivity(Intent intent) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        int result;
        try {
            // 將intent引數序列化,寫入data中
            intent.writeToParcel(data, 0);
            // 呼叫BinderProxy物件的transact方法,交由Binder驅動處理。
            mRemote.transact(IActivityManager.TRANSACTION_startActivity, data, reply, 0);
            reply.readException();
            // 等待server執行結束後,讀取執行結果
            result = reply.readInt();
        } finally {
            data.recycle();
            reply.recycle();
        }
        return result;
    }
}

最後,實現Binder本地物件(IActivityManager接口):

public class ActivityManagerService extends ActivityManagerNative {
    @Override
    public int startActivity(Intent intent) throws RemoteException {
        // 啟動activity
        return 0;
    }
}

簡化版的ActivityManagerService到這裡就已經實現了,剩下就是Client需要獲取到AMS的代理物件IActivityManager就可以通信了。實際開發過程中通過aidl檔案能夠自動編譯出中間代碼,並不需要我們手動去實現,不過手動編碼能夠加深對Binder機制的理解。在開發過程中我們也並不會直接使用到AMS,但瞭解AMS實現原理對熟悉Framework來說必不可少,關於AMS具體實現原理,我會在後續的文章中分析。

至此,Binder機制的基本通信過程就介紹完了,由於Binder機制太過複雜,本人水平有限,文中難免出現錯誤或不足之處,歡迎大家指正。

參考資料

寫這篇文章學習了很多資料,整篇文章的思維結構和結構圖在很大程度上都參考了下麵的文章,真誠感謝各位作者。

  • Android Binder設計與實現 – 設計篇

  • Binder學習指南

  • 寫給 Android 應用工程師的 Binder 原理剖析

  • Android行程間通信(IPC)機制Binder簡要介紹和學習計劃

  • 為什麼 Android 要採用 Binder 作為 IPC 機制?

  • Linux 虛擬記憶體和物理記憶體的理解

  • 認真分析mmap:是什麼 為什麼 怎麼用

  • 用戶空間與內核空間

  • Android 接口定義語言 (AIDL)

  • ServiceManager行程守護


●編號353,輸入編號直達本文

●輸入m獲取到文章目錄

推薦↓↓↓

Java編程

更多推薦18個技術類公眾微信

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

赞(0)

分享創造快樂