Android8 binder源码学习分析笔记(一)

发布于:2025-09-05 ⋅ 阅读:(19) ⋅ 点赞:(0)

Binder简介

Binder 是 Android 系统中最核心的进程间通信(IPC)机制。 它允许运行在不同进程中的应用程序组件(如 Activity 和 Service)安全、高效地相互调用方法、传递数据和发送消息。

我们可以把它想象成 Android 系统的“神经系统”,负责连接和协调各个不同的“器官”(进程)。

为什么需要 Binder?

  1. 进程隔离:出于安全性和稳定性的考虑,Android 中的每个应用都运行在自己独立的进程里,拥有自己独立的内存空间。一个应用不能直接访问另一个应用的内存。

  2. 通信需求:但应用之间又经常需要协作。例如:

    • 一个应用需要调用系统服务(如获取位置信息、打开摄像头)。

    • 你自己的应用需要与一个后台运行的 Service 通信。

    • 两个不同的应用需要共享数据。

Binder 就是为了在 保证进程隔离 的前提下,安全高效地实现进程间通信 而诞生的。

Binder 的关键优势

  1. 高性能:相比传统的 Linux IPC(如管道、消息队列、Socket),Binder 只需要一次数据拷贝,效率非常高。这是它被选为 Android 核心机制的重要原因。

  2. 安全性:Binder 机制继承了进程本身的用户ID(UID)和进程ID(PID),从而可以方便地由系统进行身份校验和权限控制。系统服务可以知道是哪个进程发来的请求,并决定是否允许该操作。

  3. 面向对象:开发者感觉像是在调用本地对象的方法一样简单,所有复杂的序列化、反序列化和传输过程都被框架隐藏了。开发者通常通过 AIDL(Android Interface Definition Language) 来定义接口,让编译器自动生成上述的代理和实体代码。

AIDL简介

AIDL(Android Interface Definition Language)是一种接口定义语言。它的唯一目的就是为了让我们能够方便地利用Binder机制来实现跨进程通信(IPC)

你可以把它看作一份双方都必须遵守的“合同”或“协议”。这份合同由你来起草,它明确规定了:

  • 服务端能提供什么方法?(方法名)

  • 客户端需要传递什么参数?(参数类型和方向:输入in、输出out、输入输出inout

  • 服务端会返回什么结果?(返回值类型)

Android SDK工具会根据这份“合同”(.aidl文件)自动生成隐藏的Java代码,这些代码封装了所有繁琐的Binder通信细节(如序列化、反序列化、收发数据等)。这样,开发者就可以专注于业务逻辑,而不必去手动实现复杂的Binder代理(Proxy)和存根(Stub)。aidl文件拥有自己的语法,不过和java语言比较类似。

为什么需要AIDL?

虽然Binder很强大,但如果要你手动编写所有用于Binder通信的代码(代理类、存根类、序列化/反序列化逻辑),那将是非常繁琐、容易出错且极其耗时的工作。

AIDL就是为了自动化这个过程而生的。你只需要定义好接口,Android构建系统就会为你生成所有“模板代码”。

应用层AIDL生成工具使用案例及源码分析

首先,在Android Studio中新建一个.aidl文件。

注意:高版本的Android Studio需要现在gradle中配置BuildFeature支持AIDL,否则将不能创建.aldl文件。

然后在生成的AIDL文件中写自己想定义的接口。

再点击Build,就会build目录下生成一个java格式的aidl文件。可以看到这个文件有100多行代码,而在我们的.aidl文件中只写了三行代码。这个AIDL生成工具还是十分好用的。

那我们来分析一下这个java文件。首先定义了一个默认的IMyAidlInterface接口,继承自所有binder接口的基类android.os.IInterface。然后在这个接口中拥有两个静态类Default和Stub。在Default类中是对接口方法的默认实现。在Stub类中,它的内部构造方法把这个接口对象和描述符相绑定。

Binder.java中的方法:
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}

继续分析这个Stub静态类,在这个类中有个静态方法asInterface。在这个方法中首先会调用Binder.java的queryLocalInterface方法来查询这个binder是否处于本进程中。如果存在的话就直接返回这个Stub本身。不存在的话则去创建一个Proxy静态类对象,Proxy会将调用打包,通过binder驱动发送给服务端。

Binder.java中的queryLocalInterface方法,可以看到就是获取之前传递的binder接口对象

public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
        if (mDescriptor != null && mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

那么我们来看看这个静态类Proxy。在这个类中,会获取远程的Binder对象,在调用getServiceName时,并不是执行远程逻辑,而是将参数打包成Parcel然后通过binder远程发送给服务端,并且读取服务端的回复。在这个例子中,首先调用Parcel的obtain方法获取一个Parcel对象,然后写入数据,接下来调用transact方法将数据发送,然后服务端的onTransact方法会根据传递的事务ID调用子类重写的getServiceName方法,然后将数据返回,那么在Proxy中就能读取到返回值。

下面为AIDL工具生成的完整的java文件。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * ... (AIDL 工具生成信息,说明了生成命令和参数)
 */
package com.example.myapplication.aidl;

// 这里可以导入非默认类型(例如自定义的 Parcelable 类型)
// import ...

// 定义 AIDL 接口。它继承自 android.os.IInterface,这是所有 Binder 接口的基类。
public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * Default implementation for IMyAidlInterface.
     * 这是一个默认的接口实现,所有方法返回空或默认值。
     * 通常用于服务端的本地实现,或者作为基类。在实际应用中,你会继承这个类并重写方法。
     */
    public static class Default implements com.example.myapplication.aidl.IMyAidlInterface {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         * AIDL 中定义的方法。这里只是一个示例实现,返回 null。
         */
        @Override
        public java.lang.String getServiceName(int serviceId) throws android.os.RemoteException {
            return null; // 默认实现,返回 null
        }

        @Override
        public android.os.IBinder asBinder() {
            return null; // 默认实现,返回 null
        }
    }

    /**
     * Local-side IPC implementation stub class.
     * 这是一个抽象的 Stub 类,继承自 Binder 并实现了 IMyAidlInterface。
     * 这是服务端的基础实现类。服务端的实际实现通常会继承这个 Stub 类。
     */
    public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IMyAidlInterface {
        /**
         * Binder 的唯一标识符,通常是接口的全限定名。
         */
        public static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IMyAidlInterface";

        /**
         * 事务 ID,用于标识在 Binder 通信中调用的方法。
         * 每个 AIDL 定义的方法都会有一个对应的 TRANSACTION_* 常量。
         * 注意:这里的值必须与客户端 Proxy 类中的值保持一致。
         */
        static final int TRANSACTION_getServiceName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        /** Construct the stub at attach it to the interface. */
        @SuppressWarnings("this-escape")
        public Stub() {
            // 将接口描述符和当前实例关联到 Binder 对象。
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.myapplication.aidl.IMyAidlInterface interface,
         * generating a proxy if needed.
         * 这是一个工厂方法,用于将 IBinder 对象转换为 IMyAidlInterface 接口。
         * 如果 IBinder 来自同一个进程(即本地调用),则直接返回 Stub 对象本身。
         * 如果来自不同进程(远程调用),则返回一个封装了 IBinder 的 Proxy 对象。
         */
        public static com.example.myapplication.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            // 查询本地接口,检查 IBinder 对象是否在当前进程。
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.myapplication.aidl.IMyAidlInterface))) {
                // 如果在同一个进程,直接返回 Stub 对象。
                return ((com.example.myapplication.aidl.IMyAidlInterface) iin);
            }
            // 如果在不同进程,返回一个代理对象(Proxy)。
            return new com.example.myapplication.aidl.IMyAidlInterface.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this; // 返回当前 Binder 对象本身。
        }

        /**
         * 处理跨进程调用的事务。
         * 运行在服务端的 Binder 线程池中。
         * @param code 事务 ID,标识要调用的方法(例如 TRANSACTION_getServiceName)。
         * @param data 包含调用参数的 Parcel 对象。
         * @param reply 用于写入返回值的 Parcel 对象。
         * @param flags 操作标志,0 表示普通 RPC。
         * @return 返回 true 表示事务处理成功,false 表示未知事务。
         */
        @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;
            // 确保数据包中包含正确的接口描述符
            if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
                data.enforceInterface(descriptor);
            }
            if (code == INTERFACE_TRANSACTION) { // 查询接口描述符的事务
                reply.writeString(descriptor);
                return true;
            }
            // 根据事务 ID 分发到具体的方法
            switch (code) {
                case TRANSACTION_getServiceName: {
                    // 读取参数
                    int _arg0;
                    _arg0 = data.readInt();
                    // 调用实际的方法实现(子类重写的方法)
                    java.lang.String _result = this.getServiceName(_arg0);
                    reply.writeNoException(); // 写入无异常标志
                    reply.writeString(_result); // 写入返回值
                    break;
                }
                default: {
                    // 未知事务,交由父类处理
                    return super.onTransact(code, data, reply, flags);
                }
            }
            return true; // 事务处理完成
        }

        /**
         * 代理类 (Proxy),用于客户端。
         * 当客户端和服务端不在同一进程时,客户端拿到的接口对象实际上是这个 Proxy 的实例。
         * Proxy 会将方法调用打包,通过 Binder 驱动发送给服务端。
         */
        private static class Proxy implements com.example.myapplication.aidl.IMyAidlInterface {
            private android.os.IBinder mRemote; // 远程 Binder 对象的引用

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

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

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * 客户端调用的方法。它并不真正执行逻辑,而是将参数打包,通过 Binder 发送事务。
             */
            @Override
            public java.lang.String getServiceName(int serviceId) throws android.os.RemoteException {
                // 准备输入和输出的 Parcel 对象
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR); // 写入接口描述符
                    _data.writeInt(serviceId); // 写入参数
                    // 通过 Binder 发送事务,并等待回复
                    // TRANSACTION_getServiceName 是事务 ID,必须与 Stub 中定义的一致
                    // _data 是发送的数据
                    // _reply 是接收回复的数据
                    // 0 是标志位,通常为 0
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getServiceName, _data, _reply, 0);
                    // 读取回复中的异常(如果有的话,会抛出 RemoteException)
                    _reply.readException();
                    // 读取返回值
                    _result = _reply.readString();
                } finally {
                    // 回收 Parcel 对象,避免内存泄漏
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     * 这是 AIDL 文件中定义的方法声明。
     * 实际实现由 Stub 的子类(服务端)或 Proxy(客户端)完成。
     */
    public java.lang.String getServiceName(int serviceId) throws android.os.RemoteException;
}

从transact到onTransact

继续分析一下transact的源码和onTransact的源码。在transact方法中,会调用本地的onTransact方法,然后执行重写的onTransact的业务逻辑。

/**
 * 执行一个Binder事务。
 * 这是Binder通信的核心方法,用于向Binder对象发送请求并可选地接收回复。
 *
 * @param code  事务标识符,指明要执行的操作。由接口定义(如AIDL中的TRANSACTION_*)。
 * @param data  包含调用参数的Parcel对象。不能为null。
 * @param reply 用于接收返回值的Parcel对象。如果为null,表示这是一个单向调用(oneway)。
 * @param flags 额外的操作标志。0表示普通RPC,FLAG_ONEWAY表示单向调用。
 * @return      返回true表示事务成功完成,false通常表示未能识别事务代码。
 * @throws RemoteException 如果事务执行过程中发生错误。
 */
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
        int flags) throws RemoteException {
    
    // 调试日志,通常在发布版本中 if (false) 会被编译器优化掉
    if (false) Log.v("Binder", "Transact: " + code + " to " + this);

    // 确保数据 parcel 的读取位置重置到起始处,这是一个安全措施
    // 因为传入的data可能已经被写入数据,我们需要从开头读取
    if (data != null) {
        data.setDataPosition(0);
    }
    
    // 关键调用:将事务派发给 onTransact 方法处理
    // 注意:这里调用的是 this.onTransact,对于本地Binder对象,这就是最终处理点
    // 对于远程代理,这个transact方法会被重写,通过Binder驱动发送到远程进程
    boolean r = onTransact(code, data, reply, flags);
    
    // 如果存在回复 parcel,将其读取位置重置到起始处,方便调用者读取结果
    if (reply != null) {
        reply.setDataPosition(0);
    }
    
    // 返回事务执行结果
    return r;
}

/**
 * 处理一个传入的Binder事务。
 * 这是一个默认的存根实现,仅返回false。你需要重写此方法来实现具体的事务解组逻辑。
 *
 * <p>如果你想调用这个方法,应该使用 transact()。
 *
 * <p>实现者在返回结果时通常应该使用 
 * {@link Parcel#writeNoException() Parcel.writeNoException} 和
 * {@link Parcel#writeException(Exception) Parcel.writeException} 
 * 来将异常传播回调用者。
 *
 * @param code   要执行的操作。应该是 {@link #FIRST_CALL_TRANSACTION} 和 
 *               {@link #LAST_CALL_TRANSACTION} 之间的数字。
 * @param data   从调用者接收到的已组编的数据。
 * @param reply  如果调用者期望返回结果,应该将结果组编到这里。
 * @param flags  额外的操作标志。0表示普通RPC,或 {@link #FLAG_ONEWAY} 表示单向RPC。
 * @return       成功调用返回true;返回false通常表示未能识别事务代码。
 */
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
        int flags) throws RemoteException {
    
    // 处理接口描述符查询事务
    if (code == INTERFACE_TRANSACTION) {
        reply.writeString(getInterfaceDescriptor());
        return true;
    } 
    // 处理dump诊断事务
    else if (code == DUMP_TRANSACTION) {
        ParcelFileDescriptor fd = data.readFileDescriptor();
        String[] args = data.readStringArray();
        if (fd != null) {
            try {
                // 执行dump操作,通常用于输出调试信息
                dump(fd.getFileDescriptor(), args);
            } finally {
                IoUtils.closeQuietly(fd);
            }
        }
        // 写入StrictMode头部信息
        if (reply != null) {
            reply.writeNoException();
        } else {
            StrictMode.clearGatheredViolations();
        }
        return true;
    } 
    // 处理shell命令事务
    else if (code == SHELL_COMMAND_TRANSACTION) {
        ParcelFileDescriptor in = data.readFileDescriptor();
        ParcelFileDescriptor out = data.readFileDescriptor();
        ParcelFileDescriptor err = data.readFileDescriptor();
        String[] args = data.readStringArray();
        ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
        ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
        try {
            if (out != null) {
                // 执行shell命令
                shellCommand(in != null ? in.getFileDescriptor() : null,
                        out.getFileDescriptor(),
                        err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
                        args, shellCallback, resultReceiver);
            }
        } finally {
            IoUtils.closeQuietly(in);
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(err);
            // 写入StrictMode头部信息
            if (reply != null) {
                reply.writeNoException();
            } else {
                StrictMode.clearGatheredViolations();
            }
        }
        return true;
    }
    
    // 默认返回false,表示不识别此事务代码
    // 子类应该重写此方法以处理自定义事务
    return false;
}


网站公告

今日签到

点亮在社区的每一天
去签到