---
# System prepended metadata

title: AIDL & Binder
tags: [Android 進階, aidl, binder]

---

---
title: 'AIDL & Binder'
disqus: kyleAlien
---

AIDL & Binder
===

## OverView of Content

如有引用參考請詳註出處，感謝 :smile:

> Binder 是 Android 進程通訊的特色，包括 APP 的啟動 AMS 應用管理都有使用到 Binder，以下我們主要通過 AIDL 來了解 Binder

[TOC]

## 多進程溝通
* Binder 是 Android 開發，多進程溝通的其中一種方式，不同進程之間是不能相互通訊的，而進程間溝通有也有其他的方式，每種方式所擁有的特性也不同，**Socket、信號、信號量、內存共享、管道**

| 溝通方式 | 性能（工作方式） | 特點 | 安全性 |
| -------- | -------- | -------- | - |
| Socket | 須拷貝 2 次 | 使用 C/S 架構，擁有相同接口 | 依賴上層協議，訪問接口開放 |
| 內存共享 | **無須拷貝** | 不易控制，需要作到許多的同步動作 | 依賴上層協議，訪問接口開放 |
| Binder | 須拷貝 1 次 | 使用 C/S 架構，擁有相同接口 | **PID 是系統分配，較安全** |
> **基於上層協議**，代表了是依靠上層來給予 Port / PID 這些 key，所以較不安全 

### 進程通訊功能
1. 空間：每個進程所擁有的空間是有限制的，要拓展其功能就必須使用到多進程

2. 體驗：減少程序崩潰導致 APP 整個無法運作的狀況，因為屬於不同的進程，令一個進程的崩潰不會導致主要的進程一起崩潰


### 傳統進程共享 ＆ Android Binder
1. 內存共享：**無須拷貝**
    > ![](https://i.imgur.com/L8Xr8U9.png)

2. Socket：**需要兩次拷貝**
    > ![](https://i.imgur.com/pENyPwO.png)

3. Binder：**需要拷貝一次**
    > ![](https://i.imgur.com/SBMKmKA.png)


## AIDL 概述
**AIDL 是 Android 基於 Binder 做出的功能，AIDL 有以下特點**

1. 文件類型：**副檔名為 .aidl**

2. 數據類型：**除了基礎類型以外的類型都需要 import 導入該類，就算在 ++同一資料夾內也需要++**
    :::info
    * AIDL 基礎 8 類 byte、short、int、long、float、double、boolean、char
    String 類型
    CharSequence 類型

        **集合類內部的元素**必須是 `aidl 支持的類型` or `aidl 生成的接口` or `定義為 parcelable` 
        > List 類型，**可以使用泛型**
    Map 類型，**不可使用泛型**
    :::

3. **方向標示 Tag**：有分為三種，^1^ in、^2^ out、^3^ inout，這些 Tag 是控制數據的流向

    ^1^ **String & CharSequence 只能為 in**，**in 時數據是 ++使用拷貝++**
    ^2^ **out 時就會同步數據，服務端修改會影響客戶端**

    | 方向標示 Tag | 說明 | 備註 |
    | -------- | -------- | -------- |
    | in | 數據只能由 Binder Client 流向 Binder Server | **String & CharSequence 只能為 in** |
    | out | 數據只能由 Binder Server 端流向 Binder Client |  |
    | inout | Binder Client & Binder Client 會同步資料 |  |
    > ![](https://i.imgur.com/UQzGIzv.png)

4. 明確包名：在 AIDL 文件文件中需要明確聲明數據類型的包名，需要對照 Java 文件的資料結構，**也就是相同的包名底下**（後面會看到範例說明）

### AIDL 文件類型 - Parcelable
* AIDL 文件可以分類為兩塊，^1^ 對非基礎的類實現 Parcelable 接口、^2^ 定義接口方法，聲明要暴露哪些接口給客戶端調用（使用定向 tag 設定）
    | 方向標示 Tag | Parcelable 的對應函數 | 備註 |
    | -------- | -------- | -------- |
    | in | writeToParcel | 預設 |
    | out | readFromParcel | 自己實現 |

* Parcelable 是一個 interface，它內部有幾個一定要實現的方法，**==預設生成的模板類只支持 in 的方向標示==，因為它內部有一定要實現 writeToParcel 方法**
    ```java=
    // Parcelable interface 
    public interface Parcelable {
        int CONTENTS_FILE_DESCRIPTOR = 1;
        int PARCELABLE_WRITE_RETURN_VALUE = 1;

        int describeContents();
        
        // in 
        void writeToParcel(Parcel var1, int var2);

        public interface ClassLoaderCreator<T> extends Parcelable.Creator<T> {
            T createFromParcel(Parcel var1, ClassLoader var2);
        }

        public interface Creator<T> {
            T createFromParcel(Parcel var1);

            T[] newArray(int var1);
        }
    }
    ```
* **如果要實現 out 方向標示就要自己寫一個 readFromParcel 方法，==讀取的++順序一定要和寫入的順序相同++==**
    ```java=
    // in 方向
    @Override
    public void writeToParcel(Parcel p, int flags) {
        p.writeString(name);
        p.writeInt(age);
    }

    // out 方向
    public void readFromParcel(Parcel p) {
        name = p.readString();
        age = p.readInt();
    }
    ```
* 如上實現定義後就可以使用 in / out / inout 方向 Tag
    ```java=
    public class Person implements Parcelable {
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        protected Person(Parcel in) {
            this.name = in.readString();
            this.age = in.readInt();
        }

        public static final Creator<Person> CREATOR = new Creator<Person>() {
            @Override
            public Person createFromParcel(Parcel in) {
                return new Person(in);
            }

            @Override
            public Person[] newArray(int size) {
                return new Person[size];
            }
        };

        @Override
        public int describeContents() {
            return 0;
        }


        // in 方向
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(age);
        }

        // out 方向
        public void readFromParcel(Parcel p) {
            name = p.readString();
            age = p.readInt();
        }

        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", grade=" + grade +
                    '}';
        }

        @Override
        public String toString() {
            return "Name: " + name + ", age: " + age;
        }
    }
    ```

### AIDL Service 創建
* 目前我這裡使用在相同的 Project 中呼叫在不同進程的 Server，所以這邊是使用創建另外一個 Service Module，並依賴在主 Module 中
    * 主 Moudle gradle
        ```groovy=
        // 主 Moudle 依賴 Service Module
        dependencies {
            ...

            implementation project(":remote_service")
        }
        ```
        
    * Service gradle（Module 取名為 remote_service）
        ```groovy=
        // 依賴 Module 必須修改為 library
        plugins {
            id 'com.android.library'
        }

        android {
            compileSdkVersion 30
            buildToolsVersion "30.0.3"

            defaultConfig {
                // 必須移除 applicationId，因為一個應用只能有一個 applicationId
                // applicationId "com.example.mytest_aidl"
                minSdkVersion 21
                targetSdkVersion 30
                ...
            }

            ...
        }
        ```

* 在 src 資料夾上右鍵 -> new -> AIDL -> AIDL File 就可創建（自己手動創建也可以），之後**使用 rebuild 就可以自動創建** 需要的程式

    1. 創建一個名為 IRemoteService.aidl 檔案
        > ![](https://i.imgur.com/5EobkFv.png)

    2. 讓 Java 層需要傳輸的物件實現 Parcelable 接口
        ```java=
        // 這裡的包就很重要
        package com.example.remote_service.info;
        
        public class BookData implements Parcelable {

            private String name;
            private int pageCount;
            private int starLevel;

            public String getName() {
                return name;
            }

            public BookData setName(String name) {
                this.name = name;
                return this;
            }

            public int getPageCount() {
                return pageCount;
            }

            public BookData setPageCount(int pageCount) {
                this.pageCount = pageCount;
                return this;
            }

            public int getStarLevel() {
                return starLevel;
            }

            public BookData setStarLevel(int starLevel) {
                this.starLevel = starLevel;
                return this;
            }

            @NonNull
            @Override
            public String toString() {
                return "Name: " + name + "\n" +
                        "pageCount: " + pageCount + "\n" +
                        "starLevel: " + starLevel;
            }

            public BookData() { }

            protected BookData(Parcel in) {
                readFromParcel(in);
            }

            public static final Creator<BookData> CREATOR = new Creator<BookData>() {
                @Override
                public BookData createFromParcel(Parcel in) {
                    return new BookData(in);
                }

                @Override
                public BookData[] newArray(int size) {
                    return new BookData[size];
                }
            };

            @Override
            public int describeContents() {
                return 0;
            }

            // tag: in, client to service
            @Override
            public void writeToParcel(Parcel dest, int flags) {
                dest.writeString(name);
                dest.writeInt(pageCount);
                dest.writeInt(starLevel);
            }

            // tag: out, service to client
            public void readFromParcel(Parcel in) {
                name = in.readString();
                pageCount = in.readInt();
                starLevel = in.readInt();
            }

        }
        ```

    3. Server 創建另外一個 BookData.aidl 文件，並引入對應需要傳輸的 Java 類
        ```java=
        // 這裡要注意使用對應的包名，需要與要對應的 Java 文件相同
        package com.example.remote_service.info;

        parcelable BookData;
        ```
        :::warning 
        * aidl 資料夾下不可以 import java 檔案
            > ![](https://i.imgur.com/6IeSmDQ.png)

        * 解法：
            1. Aidl 資料結構要與 Java 相同，以下就是都放置在 info 資料夾下
                > ![](https://i.imgur.com/Xe5B9CO.png)

            2. 手動修改 package 路徑（該路徑指定 java 實現檔案的位置）
                > ![](https://i.imgur.com/hNXGDug.png)

        :::

    4. Server 在 IRemoteService.aidl 檔案中加入相對應要讓 Client 使用的方法
        ```java=
        package com.example.remote_service;

        import com.example.remote_service.info.BookData;

        interface IRemoteService {

            List<BookData> getBookData();

            void addBookData(inout BookData book);


            // 流向控制
            void addBookDataIn(in BookData book);

            void addBookDataOut(out BookData book);

        }
        ```

    :::warning
    * **由於取不同類名，aidl 找不到 .java 文件**
        > Android 是使用 gradle 的架構來建立 project 的，而 **Gradle 預設 Java 程式的訪問路徑就在 java 包下**，這也就是為何 .java 文件放置在 aidl 包下會找不到的原因，但**可以透過在 ++gradle 中設置 sourceSets++ 找查路徑來修正 (不建議)**

        ```groovy=
        // 讓 gradle 在 aidl 的路徑下找尋 .java 文件，sourceSets 必須設置在 app gradle 中
        android {
            ...

            sourceSets {
                main {
                    java.srcDirs = ['src/main/java', 'src/main/aidl']
                }
            }
        }
        ```
    :::
    
### AIDL 生成檔案
* **其實可以把 AIDL 檔案當成是一個 ++Script（腳本） 檔案++**，把它稱為腳本是因為，經過編譯過經過後就會產生新的檔案給使用者使用

* 在上面的 AIDL Service 創建 完成後，點擊 Build 後 IDE 就會生成新的檔案（build/generated/aidl_source_output_dir 資料夾下）
    > ![](https://i.imgur.com/l4RNRsl.png)

* 以下是 IDE 自動生成的檔案，列出幾個重點 ＆ UML 關係圖
    > ![](https://i.imgur.com/w0cxGKX.png)
    ```java=
    // IDE 自動生成的檔案

    public interface IRemoteService extends android.os.IInterface
    {
      ...

      // Server 端需要實現
      public static abstract class Stub extends android.os.Binder implements com.example.remote_service.IRemoteService
      {
        private static final java.lang.String DESCRIPTOR = "com.example.remote_service.IRemoteService";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
          this.attachInterface(this, DESCRIPTOR);
        }

        public static com.example.remote_service.IRemoteService asInterface(android.os.IBinder obj)
        {
          if ((obj==null)) {
            return null;
          }
          android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
          if (((iin!=null)&&(iin instanceof com.example.remote_service.IRemoteService))) {
            return ((com.example.remote_service.IRemoteService)iin);
          }
          return new com.example.remote_service.IRemoteService.Stub.Proxy(obj);
        }
        @Override public android.os.IBinder asBinder()
        {
          return this;
        }

        // 複寫 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)
          {
            case INTERFACE_TRANSACTION:
            {
              reply.writeString(descriptor);
              return true;
            }
            case TRANSACTION_getBookData:
            {
              data.enforceInterface(descriptor);
              java.util.List<com.example.remote_service.info.BookData> _result = this.getBookData();
              reply.writeNoException();
              reply.writeTypedList(_result);
              return true;
            }
            case TRANSACTION_addBookData:
            {
              data.enforceInterface(descriptor);
              com.example.remote_service.info.BookData _arg0;
              if ((0!=data.readInt())) {
                _arg0 = com.example.remote_service.info.BookData.CREATOR.createFromParcel(data);
              }
              else {
                _arg0 = null;
              }
              this.addBookData(_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;
            }
            ...
          }
        }

        // 與底層 C、Cpp 溝通的代理類（跟 ProcessState 通訊）
        private static class Proxy implements com.example.remote_service.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 java.util.List<com.example.remote_service.info.BookData> getBookData() throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.util.List<com.example.remote_service.info.BookData> _result;
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              boolean _status = mRemote.transact(Stub.TRANSACTION_getBookData, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                return getDefaultImpl().getBookData();
              }
              _reply.readException();
              _result = _reply.createTypedArrayList(com.example.remote_service.info.BookData.CREATOR);
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
            return _result;
          }
          @Override public void addBookData(com.example.remote_service.info.BookData book) throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              if ((book!=null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0);
              }
              else {
                _data.writeInt(0);
              }
              boolean _status = mRemote.transact(Stub.TRANSACTION_addBookData, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                getDefaultImpl().addBookData(book);
                return;
              }
              _reply.readException();
              if ((0!=_reply.readInt())) {
                book.readFromParcel(_reply);
              }
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
          }
          ...
          
          public static com.example.remote_service.IRemoteService sDefaultImpl;
        }
        static final int TRANSACTION_getBookData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBookData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addBookDataIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_addBookDataOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        
        ... 省略部份
      }
        
      // 以下為 AIDL 編寫的抽象方法
      public java.util.List<com.example.remote_service.info.BookData> getBookData() throws android.os.RemoteException;
      public void addBookData(com.example.remote_service.info.BookData book) throws android.os.RemoteException;
      public void addBookDataIn(com.example.remote_service.info.BookData book) throws android.os.RemoteException;
      public void addBookDataOut(com.example.remote_service.info.BookData book) throws android.os.RemoteException;
    }
    ```


### Service 端實現
* 創建一個 Service 並記得在 Service 端的 AndroidManifest 中聲明

    1. 實現 Service 服務：創建一個 BookManager 類
        ```java=
        public class BookManager extends Service {

            private static final String TAG = BookManager.class.getSimpleName();

            
            // Service 端儲存的資料
            private final List<BookData> bookDataList = new ArrayList<BookData>(){
                {
                    add(new BookData().setName("Android").setPageCount(300).setStarLevel(5));
                    add(new BookData().setName("Design OOP").setPageCount(280).setStarLevel(5));
                }
            };

            private final IRemoteService.Stub stub = new IRemoteService.Stub() {
                @Override
                public List<BookData> getBookData() throws RemoteException {
                    return bookDataList;
                }

                @Override
                public void addBookData(BookData book) throws RemoteException {
                    bookDataList.add(book);
                }

                @Override
                public void addBookDataIn(BookData book) throws RemoteException {
                    if (book != null) {
                        book.setStarLevel(1);
                        bookDataList.add(book);
                        Log.e(TAG, book.getName() + " In: BookManager reset star to 1");
                    } else {
                        Log.e(TAG, "In: addBookDataIn get null object.");
                    }
                }

                @Override
                public void addBookDataOut(BookData book) throws RemoteException {
                    if (book != null) {
                        Log.e(TAG, book.getName() + " Out: Client book star");

                        book.setStarLevel(7);
                        Log.e(TAG, book.getName() + " Service change start to: 7");

                        bookDataList.add(book);
                    } else {
                        Log.e(TAG, "Out: addBookDataOut get null object.");
                    }
                }
            };

            @Nullable
            @Override
            public IBinder onBind(Intent intent) {
                return stub;
            }

        }
        ```
    2. 在 AndroidManifest 中聲明該 Service，有關詳細設定可以參照 [**官網對 service 的說明**](https://developer.android.com/guide/topics/manifest/service-element?hl=zh-cn)，這邊只給幾個會用到的設定值
        | Service 設定 | 說明 |
        | -------- | -------- |
        | enabled | 是否開啟 |
        | exported | 是否對外開放讓其他的應用使用該 Service |
        | **process** | 由於是在不同程，所以必須以 `:` 開頭設定，這樣才會跑在不同進程 |

        ```xml=
        <?xml version="1.0" encoding="utf-8"?>
        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.example.remote_service">

            <application
                android:allowBackup="true"
                android:label="@string/app_name"
                android:supportsRtl="true"
                android:theme="@style/Theme.MyTest_aidl">
                
                <!-- 設定開啟並對外開放 -->
                <!-- 這裡是同 Module 其實可以不用設定 exported = true -->
                <service android:name=".info.BookManager"
                    android:enabled="true"
                    android:exported="true"
                    android:process=":remote">

                    <intent-filter>
                        <action android:name="com.example.remote_service"/>
                        <category android:name="android.intent.category.DEFAULT"/>
                    </intent-filter>
                </service>

            </application>

        </manifest>
        ```

### AIDL Client 調用 Service

* aidl_client : 使用者，透過 Intent 呼叫 Service，並且使用 [**bindService**](https://hackmd.io/qwwq4oLMTmaw2UksbpPOUg#bindService--unbindService) 方式獲取對服務的操作

    :::info
    * **asInterface 功能**
        會依照你是否在同一個進程中創建不同的對象，**若是不同進程就需要創建 Proxy 類**
    :::

```java=
public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    private IRemoteService remoteService;

    private final ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remoteService = IRemoteService.Stub.asInterface(service);

            try {
                remoteService.addBookData(new BookData().setName("HelloWorld").setPageCount(123).setStarLevel(2));

                Log.i(TAG, "-----------------------------------------------------------");
                for(BookData book : remoteService.getBookData()) {
                    Log.i(TAG, "Book: " + book.toString());
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startRemoteService();
    }

    private void startRemoteService() {
        Intent intent = new Intent(this, BookManager.class);
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }

}
```
:::success
* 若服務在不同應用
    必須明確寫出包名 ＆ Action
    ```java=
    private void startRemoteService() {
        Intent  = new Intent();
        // ComponentName（<pkg 包名>, <完整 class 名>）
        ComponentName name = new ComponentName("com.example.aidl_service",
                "com.example.aidl_service.AlienService");
        intent.setComponent(name);

        // 或是以下用法
        // intent.setAction("com.example.aidl_service.AlienService");
        // intent.setPackage("com.example.aidl_service");

        bindService(intent, connection, BIND_AUTO_CREATE);
    }
    ```
:::

### AIDL 程式分析
```java=
// 簡單架構概念
public interface IRemoteService extends android.os.IInterface {

    public abstract class Stub extends android.os.Binder implements IRemoteService {

        public static class Proxy implements IRemoteService {

        }
    }
}
```

* 關係圖
    > ![](https://i.imgur.com/c4c30Ao.png)


```java=
public interface IRemoteService extends android.os.IInterface {
    /**
     * Default implementation for IRemoteService.
     */
     ... 忽略 Default

    /**
     * Local-side IPC implementation stub class.
     */
     // "1. "
    public static abstract class Stub extends android.os.Binder implements com.example.remote_service.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.example.remote_service.IRemoteService";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.example.remote_service.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            // "3. "
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.remote_service.IRemoteService))) {
                return ((com.example.remote_service.IRemoteService) iin);
            }
            return new com.example.remote_service.IRemoteService.Stub.Proxy(obj);
        }

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

        // 與 transact 的方法有相同的引數
        @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_getBookData: {
                    data.enforceInterface(descriptor);
                    java.util.List<com.example.remote_service.info.BookData> _result = this.getBookData();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBookData: {
                    data.enforceInterface(descriptor);
                    com.example.remote_service.info.BookData _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.remote_service.info.BookData.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBookData(_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;
                }
                case TRANSACTION_addBookDataIn: {
                    data.enforceInterface(descriptor);
                    com.example.remote_service.info.BookData _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.remote_service.info.BookData.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBookDataIn(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_addBookDataOut: {
                    data.enforceInterface(descriptor);
                    com.example.remote_service.info.BookData _arg0;
                    _arg0 = new com.example.remote_service.info.BookData();
                    this.addBookDataOut(_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;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
        
        // "2. "
        private static class Proxy implements com.example.remote_service.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 java.util.List<com.example.remote_service.info.BookData> getBookData() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.remote_service.info.BookData> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getBookData, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getBookData();
                    }
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.remote_service.info.BookData.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBookData(com.example.remote_service.info.BookData book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    boolean _status = mRemote.transact(Stub.TRANSACTION_addBookData, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().addBookData(book);
                        return;
                    }
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        book.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void addBookDataIn(com.example.remote_service.info.BookData book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    boolean _status = mRemote.transact(Stub.TRANSACTION_addBookDataIn, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().addBookDataIn(book);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void addBookDataOut(com.example.remote_service.info.BookData book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_addBookDataOut, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().addBookDataOut(book);
                        return;
                    }
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        book.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            public static com.example.remote_service.IRemoteService sDefaultImpl;
        }

        // 5.
        static final int TRANSACTION_getBookData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBookData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addBookDataIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_addBookDataOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        
        ...
    }

    public java.util.List<com.example.remote_service.info.BookData> getBookData() throws android.os.RemoteException;

    public void addBookData(com.example.remote_service.info.BookData book) throws android.os.RemoteException;

    public void addBookDataIn(com.example.remote_service.info.BookData book) throws android.os.RemoteException;

    public void addBookDataOut(com.example.remote_service.info.BookData book) throws android.os.RemoteException;
}
```
1. **==Stub 類是給 服務端 使用==**，所以**在 Service 內匿名實現抽象類 Stub**，而 Stub 實現了 IPerson 的方法，所以在匿名抽象類中也要實做
    ```java=
    public class BookManager extends Service {

        ... 

        private final IRemoteService.Stub stub = new IRemoteService.Stub() {
            @Override
            public List<BookData> getBookData() throws RemoteException {
                return bookDataList;
            }

            @Override
            public void addBookData(BookData book) throws RemoteException {
                bookDataList.add(book);
            }

            @Override
            public void addBookDataIn(BookData book) throws RemoteException {
                if (book != null) {
                    book.setStarLevel(1);
                    bookDataList.add(book);
                    Log.e(TAG, book.getName() + " In: BookManager reset star to 1");
                } else {
                    Log.e(TAG, "In: addBookDataIn get null object.");
                }
            }

            @Override
            public void addBookDataOut(BookData book) throws RemoteException {
                if (book != null) {
                    Log.e(TAG, book.getName() + " Out: Client book star");

                    book.setStarLevel(7);
                    Log.e(TAG, book.getName() + " Service change start to: 7");

                    bookDataList.add(book);
                } else {
                    Log.e(TAG, "Out: addBookDataOut get null object.");
                }
            }
        };
    }
    ```

2. **==Proxy 類是給 客戶端 使用==**，透過 asInterface 取得內部類的實體化，它就像是一個[**代理類**](https://hackmd.io/UK0V9uc9SDyLvVAyCpnuDA) 透過它來連接遠端的服務
    ```java=
    public class MainActivity extends AppCompatActivity {

        private static final String TAG = MainActivity.class.getSimpleName();

        private IRemoteService remoteService;

        private final ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                remoteService = IRemoteService.Stub.asInterface(service);

                try {
                    remoteService.addBookData(new BookData().setName("HelloWorld").setPageCount(123).setStarLevel(2));

    //                remoteService.addBookDataIn(new BookData().setName("AAA").setPageCount(777).setStarLevel(3));
    //                remoteService.addBookDataOut(new BookData().setName("BBB").setPageCount(888).setStarLevel(4));

                    Log.i(TAG, "-----------------------------------------------------------");
                    for(BookData book : remoteService.getBookData()) {
                        Log.i(TAG, "Book: " + book.toString());
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.i(TAG, "onServiceDisconnected");
            }
        };

        ...
    }
    ```

3. Stub 中的 asInterface 方法會判斷目前的服務是否是同一個進程的，如果是同一進程就直接返回，不須創建代理類 Stub.Proxy
    > 這裡是客戶端使用 asInterface 所以可以看出絕對不是本地的服務，所以**客戶端所調用的 Service 方法都會跑到 Stub.Proxy 類**
    ```java=
    public static com.example.remote_service.IRemoteService asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        // 同進程
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.example.remote_service.IRemoteService))) {
            return ((com.example.remote_service.IRemoteService) iin);
        }

        // 不同進程，創建 Proxy
        return new com.example.remote_service.IRemoteService.Stub.Proxy(obj);
    }
    ```

4. 現在來看客戶端調用方法 getBookData()，它內部會調用 transact 方法傳遞，最終會傳遞到 Stub 類中的 onTransact 方法
    下圖 Class
    > ![](https://i.imgur.com/MduWeZq.png)

5. 可以看到傳輸方法時它使用了 `Stub.TRANSACTION_getPersonList`，其實就是使用兩邊 (Service & Client) 持有相同的方法 & 順序，所以**使用了一系列的 ++串列數字去標明 Function++**
    ```java=
    // 參 4 
    boolean _status = mRemote.transact(
        Stub.TRANSACTION_getPersonList,     // 協定方法
        _data, 
        _reply, 
        0        // 如果是 0 則代表了雙向流通, 1 是單向
    );


    // IBinder
    public interface IBinder {
        int FIRST_CALL_TRANSACTION  = 0x00000001;
        ...
    }

    // 協定 1 方法
    static final int TRANSACTION_getBookData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    // 協定 2 方法
    static final int TRANSACTION_addBookData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    // 協定 3 方法
    static final int TRANSACTION_addBookDataIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    // 協定 4 方法
    static final int TRANSACTION_addBookDataOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    ```

* **在 ++客戶端 client++ 可以得到以下重點**
    :::success
    1. 使用 Parcel.obtain() 獲得 **\_data（保存客戶端數據）**、**\_reply（儲存服務器返回的數據）** 兩個數據儲存容器
    2. 透過 transact() 方法傳輸數據到服務端，寫入數據到 \_data (數據拷貝)
    3. 讀取 \_reply 來接收服務端返回的數據 
    :::

* **在 ++服務端 Service++ 可以得到以下重點**
    :::success
    1. 服務端會用 onTransact() 方法接收客戶端資料
    2. 判斷 function id 來執行操作呼叫服務端代碼
    3. **將要回傳的數據寫入 \_reply 引數** (數據拷貝)
    :::

## 服務源碼 - 分析
分析是用 Android 7 Source Code，使用 sourceInsight 閱讀


### 客戶端 bindService 
* 從客戶端下手，觀察他是**如何綁定 ++不同進程間++ 的服務**
```java=
// 綁定監聽
private void bindService() {
        Intent  = new Intent();
        // ComponentName（<pkg 包名>, <完整 class 名>）
        ComponentName name = new ComponentName("com.example.aidl_service",
                "com.example.aidl_service.AlienService");
        intent.setComponent(name);
        
        bindService(intent, connection, BIND_AUTO_CREATE);
    }
```
* 從 bindService 的流程就可以看出來它**使用的機制與 AIDL 相同**
1. 客戶端呼叫代理 ActivityManagerProxy
2. **++透過 ActivityManagerProxy 的 transact 方法呼叫到 ActivityManagerNative 的 onTransact 方法++**
3. 這就可以看出如何從客戶端 Client 調用到 Server 端的服務，其道理與 AIDL 相同

**--流程圖--**
> ![](https://i.imgur.com/Up1H61V.png)

* 與上面實做的 AIDL 做比較

| 類型 | Proxy 代理 | 抽象 Service 服務 |
| -------- | -------- | - |
| AIDL 生成 | ActivityManagerProxy | ActivityManagerNative |
| SDK 源碼 | IPerson.Stub.Proxy | IPerson.Stub |


### AMS 啟動服務 bindService
* 啟動服務因為有兩個變數，所以又**分為四種狀況**，**有關到 ++進程 Process++、++服務 Service++、++綁定與否++**
> 1. Process 未啟動 , Service 未啟動 （APP 未創建）
> 2. Process 啟動 , Service 未啟動 （APP 創建）
> 3. Process 啟動 , Service 啟動 , 未綁定
> 4. Process 啟動 , Service 啟動 , 已綁定
> 
> 以下介紹順序相同




**第一**、進程未啟動
> 缺少進程會進入 ActivityManagerService 中創建，先來看看流程圖 :D

![](https://i.imgur.com/OE35xPZ.png)

```java=
// ActiveServices.java
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
            ...
            
            if (app == null && !permissionsReviewRequired) {
            // 啟動 ＆ 綁定 startProcessLocked 往下追
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }
    }

        
        
// startProcessLocked 在 AMS.java 中
    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
            ...
            // start new process !!!
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            // 注意 entryPoint，需要傳入應用包包名
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
            ...
            
    }
```

**第二**、進程啟動 && 服務未啟動
> 這種狀況就缺少服務未啟動，我們先來看看啟動過程圖

**--流程圖--**
> ![](https://i.imgur.com/vUSnjrB.png)


```java=
// ActiveServices.java
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
            ...
            
            ProcessRecord app;
        
            if (!isolated) {
            ...
			// app.thread 進程啟動
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    // 綁定服務，往下跟 !!!
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                }
            } else {
                ...
            }
    }
    
    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
        ...
		// record service 紀錄
        final boolean newService = app.services.add(r);
        bumpServiceExecutingLocked(r, execInFg, "create");
        mAm.updateLruProcessLocked(app, false, null);
        mAm.updateOomAdjLocked();

        boolean created = false;
        try {
            ...
            synchronized (r.stats.getBatteryStats()) {
                // 做線程同步
                r.stats.startLaunchedLocked();
            }
            ...
            // create Service，往下跟 !!!
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            Slog.w(TAG, "Application dead when creating service " + r);
            mAm.appDiedLocked(app);
            throw e;
        } finally {
            ...
        }
        ...
    }
    
// scheduleCreateService 跳轉到 ActivityThread.java
    public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            ...
            // 使用內部類 H Handler 傳送 CREATE_SERVICE 訊息
            sendMessage(H.CREATE_SERVICE, s);
        }

// CREATE_SERVICE 最終會掉用到 handleCreateService 方法
    private void handleCreateService(CreateServiceData data) {
        ...
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            // 這個服務就是創建的我們需要的服務 !!!
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ...
        }
        ...
        
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ...

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            // attach 方法
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            // 調用 Service 的 onCreate 方法
            service.onCreate();
            // 儲存服務
            mServices.put(data.token, service);
            
            try {
                // 調用到客戶端，表示服務已經運行
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
    }
```
:::info 
1. 判斷過程中 **app(ProcessRecord) 代表了進程的紀錄**，而該類中有一個屬性 **app.thread 代表了真正的進程**
> ![](https://i.imgur.com/g4pvWF4.png)

2. **該 thread 在 ActivityThread 這個類的內部類 ApplicationThread 中，而 ActivityThread 有 main() 方法、並且有 ApplicationThread 這個屬性**
> 代表了開始運行 ActivityThread 這個類，Application 就一定不為空
```java=
public final class ActivityThread {
    
    final ApplicationThread mAppThread = new ApplicationThread();    
    ...
    
    
    public static void main(String[] args) {
        ...
        ActivityThread thread = new ActivityThread();
        ...
    }
}
```
:::

**第三/四**、Process, Service 都已經創建，差別在綁定與否
> ![](https://i.imgur.com/CUXlVKc.png)

```java=
// ActiveService.java
    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
            ...
            
            if (s.app != null && b.intent.received) {
                ...
                try {
                    c.conn.connected(s.name, b.intent.binder);
                } catch (Exception e) {
                    Slog.w(TAG, "Failure sending service " + s.shortName
                            + " to connection " + c.conn.asBinder()
                            + " (in " + c.binding.client.processName + ")", e);
                }

                ...
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                    // 差在最後一個參數 already  bind   !!!
                    requestServiceBindingLocked(s, b.intent, callerFg, true);
                }
            } else if (!b.intent.requested) {
            	// 差在最後一個參數 not bind  !!!
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }
    }
    
    
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                
                // 綁定的關鍵函數 !!!
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
                ...
            } catch (RemoteException e) {
                ...
            }
        }
        return true;
    }


/**
 * 前面有說過 r.app.thread 指的就是 ApplicationThread
 * 所以會呼叫到 ApplicationThread 中的方法
 */ 
// ActivityThread.java
public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            updateProcessState(processState, false);
            ...
            // 對 Handler 發送訊息
            sendMessage(H.BIND_SERVICE, s);
        }
        
        
// 最終會到 handleUnbindService
private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                    // 調用 Service 的 onBind 方法
                        IBinder binder = s.onBind(data.intent);
                        
                        // ActivityManagerNative.getDefault() ＝＝ ActivityManagerProxy 類
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {
                    // 調用 Service 的 reBind 方法
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }
    

/**
 * ActivityManagerService.java
 * ActivityManagerNative 的實作類就是 AMS 類
 */ 
// 
// 回傳給 Proxy 知道 publishService or serviceDoneExecuting
public void publishService(IBinder token,
            Intent intent, IBinder service) throws RemoteException {
            ...
        mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
    }

```

### AMS 客戶端 ＆ 服務端
* 經過上面的分析，我們再多加上一行，真正服務在處理的類

| 類型 | Proxy 代理 | 抽象 Service 服務 | 實現服務的類 | 
| -------- | -------- | - | - |
| AIDL 程式 | ActivityManagerProxy | ActivityManagerNative | ActivityManagerService（AMS） |
| SDK 源碼 | IPerson.Stub.Proxy | IPerson.Stub | AlienService(繼承 Service) |

* 上面看起來就有一點的混淆，現在來整理一下，畢竟**不同的應用都有相同的 AMS**，先來整理何時是調用客戶端，何時調用服務端

1. 調用 ActivityManagerService 的 **++onTransact 方法++** 區塊，就是屬於操控到 Server 端的動作，這就是屬於服務端的 AMS
> **綁定服務** 為例子 (==黃色區塊即為 AMS 服務端==)
> ![](https://i.imgur.com/xkOfU5r.png)

2. 在確定綁定後又會調用客戶端的 publishService，publishService 在調用 onTransact 調回服務端 
> ![](https://i.imgur.com/F2EhyWE.png)

3. 到這裡為止 Server 分析差不多完成，接下來會分析 Server 如何調用 Client 端的內部匿名類 ServiceConnection 

### Server 端服務回調
* 還記得我們在使用服務綁定時有傳入 3 個參數，而第 2 個參數就是 Server 監聽器，當 Server 完成所有準備工作後就會呼叫回調
```java=
// Client 端
private void bindService() {
        Intent  = new Intent();
        // ComponentName（<pkg 包名>, <完整 class 名>）
        ComponentName name = new ComponentName("com.example.aidl_service",
                "com.example.aidl_service.AlienService");
        intent.setComponent(name);
        
        bindService(intent, connection, BIND_AUTO_CREATE);
    }



// Client 監聽 Server，完成準備時就會回調
private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iPerson = IPerson.Stub.asInterface(service);
            Log.e("AIDL", "Connected to Service");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 報錯崩潰
            iPerson = null;
        }
    };
```

* 在這邊可以看到 **依監看 Server 的角度而言，++Client 這端反轉成為了 Server++**，提供給 原本的服務端 調用

**--流程圖--**
![](https://i.imgur.com/1yAPCx1.png)



```java=
/**
 * ContextImpl
 * 
 * 先看 IServiceConnection 是如何調用到 ServiceConnection 
 */ 
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            // 往下分析！ mPackageInfo 是 LoadedApk 這個類
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        ...
    }
    
    
// LoadedApk 類
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                sd = map.get(c);
            }
            if (sd == null) {
                // 往下分析
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (map == null) {
                    map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            
            // 這是最終會得到的結果
            return sd.getIServiceConnection();
        }
    }


/**
 * LoadedApk#ServiceDispatcher （內部類）
 * ServiceDispatcher 建構，觀察其屬性
 */ 
static final class ServiceDispatcher {
        // InnerConnection 
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        private final ServiceConnection mConnection;
        ...
        
}
    
// ServiceDispatcher 獲取內部屬性 mIServiceConnection
IServiceConnection getIServiceConnection() {
            return mIServiceConnection;
        }
        
        
/**
 * LoadedApk#ServiceDispatcher#InnerConnection 內部類
 * 
 * 這邊可以看到 IServiceConnection.Stub，InnerConnection 這就是我們服務的實體類 !!!
 */ 
private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service) throws RemoteException {
                // 最外部 class 
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    // 給客戶端調用
                    sd.connected(name, service);
                }
            }
        }
        

/**
 * ServiceDispatcher class
 */ 
public void connected(ComponentName name, IBinder service) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0));
            } else {
                doConnected(name, service);
            }
        }
        
public void doConnected(ComponentName name, IBinder service) {
            ...

            // If there was an old service, it is now disconnected.
            if (old != null) {
                // 回調到使用者端，通知使用者
                mConnection.onServiceDisconnected(name);
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                // 回調到使用者端，通知使用者
                mConnection.onServiceConnected(name, service);
            }
        }
```

## 結論
* 可以看的出來由於使用跨進程服務通訊，所以**不能直接通訊，轉而使用了[代理模式](https://hackmd.io/UK0V9uc9SDyLvVAyCpnuDA?view)**

| 使用者 | 使用方法 |
| -------- | -------- |
| 客戶端 | 透過 **asInterface 取得代理的對象接口** (AIDL **Proxy**)，進而實現操做 |
| 服務端 | 創建 Service 並**繼承實現抽象類(實做)** (AIDL **Stub** 的抽象類) |

* Proxy(代理)、Stub(實作) 類**都會實現共同方法接口 (AIDL 的方法)**，差別在一個是代理，一個是實作

> ![](https://i.imgur.com/OMXZBXE.png)


* 剛開始通訊實 A 為客戶端(Client)，Server 為服務端(Service)，**等服務創建完成後通知服務完成連線，兩者個關係就會==相反==過來，A 為服務端(Service)、B 為客戶端(Client)**

:::success
Binder 簡易的概念是
1. 使用 ActivityManagerNative 的 getDefault 方法得到單例的 Proxy 對象
2. Proxy 對象會透過 transact 呼叫 Native 的 onTransact 方法
3. Native 又會呼叫實際對象 (繼承 ActivityManagerNative 的對象) 的方法
:::

## Appendix & FAQ

:::info
:::

###### tags: `Android 進階` `binder` `aidl`
