【底层机制】【Android】Binder架构与原理

一、Binder 整体架构

1. 架构组成

  • Client:服务调用方
  • Server:服务提供方
  • ServiceManager:服务管理器,Android 系统启动时初始化的特殊 Binder 服务
  • Binder 驱动:内核空间的核心组件,负责进程间通信

2. 通信模型

sql 复制代码
Client Process      Kernel Space       Server Process
    |                   |                   |
    | -- IBinder.Proxy ->|                   |
    |                   | -- Binder Driver ->|
    |                   |                   | -- IBinder.Stub
    |                   | <- Transaction ---|
    | <- Result --------|                   |

二、Binder 驱动核心机制

1. 内存映射(mmap)

c 复制代码
// Binder 驱动关键数据结构
struct binder_proc {
    struct hlist_node proc_node;
    struct rb_root threads;        // 线程红黑树
    struct rb_root nodes;          // Binder 节点
    struct list_head delivered_death; // 死亡通知列表
};

struct binder_thread {
    struct binder_proc *proc;      // 所属进程
    wait_queue_head_t wait;        // 等待队列
    struct binder_transaction *transaction_stack; // 事务栈
};

mmap 工作原理

  • 进程打开 /dev/binder 设备后调用 mmap()
  • Binder 驱动在内核空间分配缓冲区
  • 同时映射到内核空间和用户空间
  • 一次拷贝机制:数据从客户端用户空间拷贝到内核缓冲区,服务端直接读取

2. 数据传输流程

  1. Client 发送数据

    • 数据打包为 binder_transaction_data
    • 通过 ioctl(BC_TRANSACTION) 发送到驱动
    • 驱动查找目标 Binder 实体
  2. Server 接收数据

    • 服务线程通过 ioctl(BC_ENTER_LOOPER) 进入循环
    • 调用 ioctl(BINDER_WRITE_READ) 读取事务
    • 驱动唤醒等待的服务线程

三、AIDL 本质解析

1. AIDL 生成的代码结构

java 复制代码
// 自动生成的 Binder 接口
public interface IMyService extends android.os.IInterface {
    // Binder 本地对象(服务端实现)
    public static abstract class Stub extends android.os.Binder implements IMyService {
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) {
            switch (code) {
                case INTERFACE_TRANSACTION:
                    reply.writeString(DESCRIPTOR);
                    return true;
                case TRANSACTION_doSomething:
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0 = data.readInt();
                    String _result = this.doSomething(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
    }

    // Binder 代理对象(客户端使用)
    private static class Proxy implements IMyService {
        private android.os.IBinder mRemote;
        
        @Override
        public String doSomething(int param) {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            String _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                _data.writeInt(param);
                mRemote.transact(Stub.TRANSACTION_doSomething, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readString();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }
}

2. AIDL 本质

  • 代码生成工具:将接口定义转换为 Binder 通信代码
  • Proxy-Stub 模式实现
    • Proxy:客户端代理,序列化参数并调用 transact()
    • Stub:服务端基类,反序列化参数并调用实际方法
  • 接口描述符机制:确保客户端和服务端接口版本一致

startActivity 的 Binder 调用全过程

一、进程内准备阶段

1. 发起调用

java 复制代码
// Activity.java
public void startActivity(Intent intent) {
    // 调用 ActivityTaskManager 服务
    ActivityTaskManager.getService().startActivity(
        mMainThread.getApplicationThread(), 
        mToken, 
        intent, 
        intent.resolveTypeIfNeeded(getContentResolver()),
        null, // resultTo
        null, // resultWho
        0,    // requestCode
        FLAG_ACTIVITY_NEW_TASK,
        null, // options
        null  // userId
    );
}

二、Binder 调用链详细流程

1. 客户端到系统进程(第一次 Binder 调用)

arduino 复制代码
Client Process (App) → System Server Process (AMS)

调用路径

  • Activity.startActivity()
  • ActivityTaskManager.getService().startActivity()
  • IActivityTaskManager.Stub.Proxy.startActivity()

Binder 事务

java 复制代码
// IActivityTaskManager.aidl 生成的 Proxy 类
public int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, ...) {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(callingPackage);
    intent.writeToParcel(data, 0);
    // ... 其他参数序列化
    mRemote.transact(TRANSACTION_startActivity, data, reply, 0);
    reply.readException();
    int result = reply.readInt();
    return result;
}

2. 系统进程处理(AMS 侧)

AMS 处理逻辑

java 复制代码
// ActivityTaskManagerService.java
public final int startActivity(IApplicationThread caller, ...) {
    return getActivityStartController().obtainStarter(intent, ...)
        .setCaller(caller)
        .execute();
}

关键步骤

  1. 权限验证:检查调用者权限
  2. Intent 解析:解析目标 Activity 信息
  3. 进程检查:判断目标 Activity 所在进程是否已启动
  4. 栈管理:确定 Activity 应该放入哪个 Task

3. 暂停当前 Activity(第二次 Binder 调用)

arduino 复制代码
System Server Process (AMS) → Client Process (App)

AMS 调用应用进程

java 复制代码
// 通过 ApplicationThread 的 Binder 接口
app.thread.schedulePauseActivity(token, finished, userLeaving, configChanges);

应用进程处理

java 复制代码
// ActivityThread.java - ApplicationThread 内部类
public final void schedulePauseActivity(IBinder token, ...) {
    sendMessage(H.PAUSE_ACTIVITY, token);
}

// H(Handler)处理消息
case PAUSE_ACTIVITY: {
    handlePauseActivity((IBinder)msg.obj, ...);
    break;
}

private void handlePauseActivity(IBinder token, ...) {
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        // 调用 Activity 的 onPause()
        performPauseActivity(r, finished, reason, pendingActions);
    }
}

4. 启动目标进程(如果需要)

进程创建流程

  1. AMS 通过 Process.start() 请求 Zygote
  2. Zygote fork 新进程
  3. 新进程入口:ActivityThread.main()

5. 启动目标 Activity(第三次 Binder 调用)

java 复制代码
System Server Process (AMS) → Target Process (App)

AMS 调用目标进程

java 复制代码
// 通过目标进程的 ApplicationThread
thread.scheduleLaunchActivity(new Intent(r.intent), ...);

目标进程处理

java 复制代码
// ActivityThread.java
private void handleLaunchActivity(ActivityClientRecord r, ...) {
    // 1. 创建 Activity 实例
    Activity activity = mInstrumentation.newActivity(
        cl, component.getClassName(), r.intent);
    
    // 2. 创建 Application(如果不存在)
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    
    // 3. 关联 Context
    activity.attach(appContext, ...);
    
    // 4. 调用 onCreate()
    mInstrumentation.callActivityOnCreate(activity, r.state);
    
    // 5. 调用 onStart()
    activity.performStart();
    
    // 6. 调用 onResume()
    activity.performResume();
}

三、Window 管理相关的 Binder 调用

1. 窗口创建(第四次 Binder 调用)

scss 复制代码
Target Process (App) → WindowManagerService (WMS)

调用流程

java 复制代码
// ActivityThread.handleResumeActivity()
final void handleResumeActivity(IBinder token, ...) {
    // 调用 Activity.onResume()
    ActivityClientRecord r = performResumeActivity(token, ...);
    
    if (r.activity.mVisibleFromClient) {
        // 添加窗口到 WMS
        ViewManager wm = r.activity.getWindowManager();
        wm.addView(decorView, layoutParams);
    }
}

// WindowManagerGlobal.addView()
public void addView(View view, ViewGroup.LayoutParams params, ...) {
    ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
    root.setView(view, wparams, panelParentView);
}

ViewRootImpl 与 WMS 通信

java 复制代码
// ViewRootImpl.java
public void setView(View view, ...) {
    // 通过 Session 与 WMS 通信
    res = mWindowSession.addToDisplay(mWindow, ...);
}

四、Binder 驱动中的关键数据结构

1. 事务处理

c 复制代码
struct binder_transaction_data {
    union {
        size_t handle;    // 对 Server 的引用
        void *ptr;        // Server 的本地地址
    } target;
    void *data.ptr;       // 数据缓冲区
    size_t data_size;     // 数据大小
};

// Binder 驱动处理事务
static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr) {
    // 查找目标 Binder 节点
    target_node = binder_get_node(proc, tr->target.handle);
    // 分配事务缓冲区
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    // 拷贝数据到目标进程
    t->buffer = binder_alloc_buf(target_proc, tr->data_size);
}

五、完整调用时序总结

scss 复制代码
[Client Process]           [Binder Driver]        [System Server]        [Target Process]
      |                           |                       |                       |
      |--- startActivity() ------>|                       |                       |
      |                           |-- TRANSACTION ------->|                       |
      |                           |                       |-- AMS.startActivity() |
      |                           |                       |                       |
      |                           |<-- schedulePauseActivity --|                  |
      |<-- onPause() ------------|                       |                       |
      |                           |                       |                       |
      |                           |                       |-- (如果需要)创建进程 -|
      |                           |                       |                       |
      |                           |<-- scheduleLaunchActivity -|                  |
      |                           |                       |                  |-- onCreate()
      |                           |                       |                  |-- onStart()  
      |                           |                       |                  |-- onResume()
      |                           |                       |                  |-- addView()
      |                           |                       |                  |   (WMS调用)

六、性能优化关键点

  1. 一次拷贝优势:相比其他 IPC(如 Socket 需要 4 次拷贝),Binder 只需 1 次用户空间到内核空间的拷贝
  2. 引用计数:Binder 对象自动管理生命周期,避免内存泄漏
  3. 线程池管理:Binder 驱动维护线程池,避免频繁创建销毁线程
  4. 死亡通知 :通过 linkToDeath() 监控 Binder 服务状态,及时清理资源

整个 startActivity 过程涉及 3-4 次主要的 Binder 调用,通过精心设计的异步回调机制和状态管理,实现了跨进程的 Activity 启动流程。

相关推荐
恋猫de小郭42 分钟前
解读 Claude 对开发者的影响:AI 如何在 Anthropic 改变工作?
android·前端·ai编程
Digitally1 小时前
如何将照片从 Mac 传输到 Android
android·macos
San301 小时前
JavaScript 底层探秘:从执行上下文看 `this` 的设计哲学与箭头函数的救赎
javascript·面试·ecmascript 6
用户41659673693551 小时前
Android 系统开发进阶:将应用配置为系统应用的完整指南
android
gis分享者1 小时前
如何在 Shell 脚本中实现字符串的截取和拼接?(容易)
面试·字符串·shell·拼接·截取
踏浪无痕1 小时前
Maven 依赖拉不下来?一文终结所有坑点
spring boot·后端·面试
Jing_Rainbow1 小时前
【LeetCode Hot 100 刷题日记(22/100)】160. 相交链表——链表、双指针、哈希表📌
算法·面试·程序员
uhakadotcom1 小时前
全面解析:GeoJSON 与 TopoJSON 的定义、差异及适用场景
前端·面试·github
三少爷的鞋1 小时前
Retrofit 核心流程模拟实现深解析
android
zhimingwen1 小时前
使用 adb shell 命令检查手机上 App的APK大小
android·adb