【Android】深入解析 Binder 源碼

前言:Binder 是什么?從類的角度來說,它是實現了 IBinder 接口的 Binder 類;從機制角度來說,它是 Android 的 IPC(進程間通信) 機制。

官方文檔:
https://developer.android.google.cn/reference/android/os/Binder

注:本文以 API 30 的 Binder 源碼進行解析。


Binder 與傳統 IPC 對比:

Binder 共享內存 Socket 管道
性能 需要拷貝一次 無需拷貝 需要拷貝兩次 需要拷貝兩次
特點 基于 C/S 架構,易用性高 控制復雜,易用性差 基于 C/S 架構,易用性差 易用性差
安全性 為每個 APP 分配 UID,同時支持實名和匿名 依賴上層協議,訪問接入點是開放的,不安全 依賴上層協議,訪問接入點是開放的,不安全 依賴上層協議,訪問接入點是開放的,不安全

了解 Binder 源碼之前首先來了解一下 AIDL,因為 APP 主要是通過 AIDL 與 Binder 機制進行通信。


一、AIDL 源碼解析

寫一個 AIDL 接口IMyAidlInterface.aidl

interface IMyAidlInterface {
    void testAidl();
}

Rebuild Project,就會在build文件夾下看到 Android Studio 為我們自動生成的代碼:

// AIDL 繼承自 IInterface,也就是說所有的 AIDL 接口都是 IInterface 的組成類。
public interface IMyAidlInterface extends android.os.IInterface {
    ...
}

看一下這個自動生成的 AIDL 的組成結構:

首先看Stub

public static abstract class Stub extends android.os.Binder implements com.tyhoowu.myapplication.IMyAidlInterface

根據源碼可知 Stub 是一個抽象類,這也就是為什么我們在寫服務端的時候需要實現 Stub 類。Stub 繼承自 Binder,實現我們自定義的 AIDL 接口。

再來看Proxy

private static class Proxy implements com.tyhoowu.myapplication.IMyAidlInterface

根據源碼可知 Proxy 直接就實現了我們自定義的 AIDL 接口。

問:兩個進程要進行通信的話,Stub 和 Proxy 哪個是負責發(fā)送,哪個負責是接收?
答:Proxy 向 Binder 發(fā)送數據,Stub 接收 Binder 發(fā)送過來的數據。

從客戶端發(fā)送到服務端,再從服務端返回的流程

1.定義唯一的類名:

private static final java.lang.String DESCRIPTOR = "com.tyhoowu.myapplication.IMyAidlInterface";

2.通過attachInterfaceStubDESCRIPTOR傳進去:

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

3.在attachInterface里,把StubDESCRIPTOR進行保存:

public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}

4.asInterface

public static com.tyhoowu.myapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
    // 判斷 IBinder 是否為 null
    if ((obj == null)) {
        return null;
    }

    // 調用 queryLocalInterface 
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    
    // 當前在同一個進程
    if (((iin != null) && (iin instanceof com.tyhoowu.myapplication.IMyAidlInterface))) {
        return ((com.tyhoowu.myapplication.IMyAidlInterface) iin);
    }

    // 服務端在本地保存 descriptor,如果客戶端和服務端不在一個進程的話,那么返回的 iin 就為空,那么此時就會 new 一個 Proxy。
    return new com.tyhoowu.myapplication.IMyAidlInterface.Stub.Proxy(obj);
}

5.queryLocalInterface

public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {

    // 將傳入的 descriptor 和 mDescriptor 進行比較,
    // 因為兩個應用有可能在同一個進程里面(自己調用自己的服務)
    if (mDescriptor != null && mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

6.Proxy

private android.os.IBinder mRemote;

// 將傳入的 remote 保存到 IBinder
Proxy(android.os.IBinder remote) {
    mRemote = remote;
}

7.testAidl

@Override
public void testAidl() throws android.os.RemoteException {
    // 通過 “池” obtain 創(chuàng)建了 _data 和 _reply
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        // 校驗
        _data.writeInterfaceToken(DESCRIPTOR);

        // 發(fā)送到 Binder,然后通知給服務端
        // 參數1:int 型,因為通信傳 String 的話會導致包過大,影響性能,客戶端傳一個數,服務端就知道這個數對應的方法。
        // 參數2:要處理的數據
        // 參數3:接收服務端返回給客戶端的數據
        // 參數4:標志位,如果是0,就表示服務端能返回數據,如果是1,就表示服務端不能返回數據。
        boolean _status = mRemote.transact(Stub.TRANSACTION_testAidl, _data, _reply, 0);

        if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().testAidl();
            return;
        }
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

8.通過Binder的一系列處理就會調用到onTransact

@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) 找到對應的方法
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(descriptor);
            return true;
        }
        case TRANSACTION_testAidl: {
            // 進行校驗
            data.enforceInterface(descriptor);
            this.testAidl();
            reply.writeNoException();
            return true;
        }
        default: {
            return super.onTransact(code, data, reply, flags);
        }
    }
}

二、Binder 源碼解析

創(chuàng)建服務的一種做法是調用bindService,以bindService流程為例來走進Binder

bindServiceContext的抽象方法:

public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags);

根據 Android 基礎,Context 下面有兩個類:

  • ContextWrapper
  • ContextImpl(Context 的實現類)

所以實際上就是調用ContextImplbindService

@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
    warnIfCallingFromSystemProcess();
    return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null, getUser());
}

ContextImplbindServiceCommon

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
        String instanceName, Handler handler, Executor executor, UserHandle user) {
    
    // 這塊實際上就是 ServiceConnection,
    // 然后會回調 ServiceConnection 的 onServiceConnected,
    // 通過 onServiceConnected,進程與服務就進行了綁定。
    IServiceConnection sd;

    ...

    try {
        ...

        // 閱讀源碼 ActivityManager.getService() 實際上返回的就是 Proxy
        // 閱讀源碼 bindIsolatedService 實際上就是調用 Proxy
        int res = ActivityManager.getService().bindIsolatedService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, instanceName, getOpPackageName(), user.getIdentifier());

        ...
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

A進程訪問B進程時的狀態(tài):

  • 進程B有沒有啟動
  • 進程B啟動了,但是里面的Service沒創(chuàng)建出來
  • 進程B啟動了,里面的Service也創(chuàng)建了,但是Service沒有被綁定過,回調onBind()
  • 進程B啟動了,里面的Service也創(chuàng)建了,但是Service已經被綁定過,回調onRebind()

總結

AIDL 接口 Stub 抽象類 Proxy 類 Stub 的實現類
IMyAidlInterface Stub Proxy new IMyAidlInterface.Stub
IActivityManager ActivityManagerNative ActivityManagerProxy ActivityManagerService

? 2020 Tyhoo Wu, All rights reserved.

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容