---
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. 內存共享:**無須拷貝**
> 
2. Socket:**需要兩次拷貝**
> 
3. Binder:**需要拷貝一次**
> 
## 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 會同步資料 | |
> 
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 檔案
> 
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 檔案
> 
* 解法:
1. Aidl 資料結構要與 Java 相同,以下就是都放置在 info 資料夾下
> 
2. 手動修改 package 路徑(該路徑指定 java 實現檔案的位置)
> 
:::
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 資料夾下)
> 
* 以下是 IDE 自動生成的檔案,列出幾個重點 & UML 關係圖
> 
```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 {
}
}
}
```
* 關係圖
> 
```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
> 
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 相同
**--流程圖--**
> 
* 與上面實做的 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

```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);
...
}
```
**第二**、進程啟動 && 服務未啟動
> 這種狀況就缺少服務未啟動,我們先來看看啟動過程圖
**--流程圖--**
> 
```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 代表了真正的進程**
> 
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 都已經創建,差別在綁定與否
> 
```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 服務端==)
> 
2. 在確定綁定後又會調用客戶端的 publishService,publishService 在調用 onTransact 調回服務端
> 
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++**,提供給 原本的服務端 調用
**--流程圖--**

```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 的方法)**,差別在一個是代理,一個是實作
> 
* 剛開始通訊實 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`