源码分析——从AIDL的使用开始理解Binder进程间通信的流程

Binder通信是 Android 系统架构的基础。本文尝试从 AIDL 的使用开始理解系统的 Binder通信。

0x00 一个 AIDL 的例子

首先我们创建一个项目,写一个 RemoteService.java,并定义个 AIDL接口 IRemoteService.aidl

interface IRemoteService {
    String getText();
}

这时候 IDE 会自动在目录 build/generated/source/aidl/debug/生成 IRemoteService.java文件。

本文为了方便调试和理解 AIDL的过程,我们把生成的 IRemoteService.java文件拷贝出来,放在 app/main/java目录下,然后把 aidl文件夹删除。

RemoteService为服务端, MainActivity为客户端。最后项目结构为

0x01 远程服务 RemoteService

public class RemoteService extends Service {
    public final static String ACTION = "net.angrycode.RemoteService";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    /**
     * 定义远程服务对外接口
     */
    IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public String getText() throws RemoteException {
            return "text from remote,pid:" + Process.myPid();
        }
    };
}

RemoteService中定义 IBinder接口,并在 onBind()方法中返回,供客户端使用。

最后在 mainifest文件中注册远程服务,指定进程为私有进程

<service android:name=".RemoteService"
    android:process=":remote">
    <intent-filter>
        <action android:name="net.angrycode.RemoteService"/>
    </intent-filter>
</service>

0x02 本地客户端 MainActivity

public class MainActivity extends AppCompatActivity {
    private TextView mTextMessage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextMessage = (TextView) findViewById(R.id.message);
    }

    public void onClickBind(View view) {
        Intent service = new Intent(this, RemoteService.class);
        service.setAction(RemoteService.ACTION);
        bindService(service, conn, Context.BIND_AUTO_CREATE);
    }
    public void onClickUnBind(View view) {
        unbindService(conn);
    }
    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IRemoteService iRemoteService = IRemoteService.Stub.asInterface(service);
            try {//连接之后获取到远程服务text
                String text = iRemoteService.getText();
                mTextMessage.setText(text);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Toast.makeText(getApplication(), "远程服务已连接", Toast.LENGTH_LONG).show();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(getApplication(), "远程服务已断开", Toast.LENGTH_LONG).show();
        }
    };
}

本地客户端实现了 ServiceConnection接口,用于监听远程服务的连接状态,并在 onServiceConnected()中拿到远程服务 RemoteService对外的接口 IRemoteService的引用。

当客户端进行绑定远程服务时,就使用 IRemoteService.Stub.asInterface(IBinder)获取到远程服务对象,客户端与服务端的通信就开始了。

0x03 IRemoteService 接口

系统自动生成的这个文件中有除了我们定义 getText()方法外还生成了两个内部类 StubProxy

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends Binder implements IRemoteService {
        private static final java.lang.String DESCRIPTOR = "net.angrycode.learnpro.IRemoteService";

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

        /**
         * Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static IRemoteService asInterface(IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof IRemoteService))) {
                return ((IRemoteService) iin);
            }
            return new IRemoteService.Stub.Proxy(obj);
        }

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

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getText: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getText();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements IRemoteService {
            private 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.lang.String getText() throws RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getText = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public java.lang.String getText() throws android.os.RemoteException;
}

Stub类继承于 Binder,但它们都实现了 IRemoteService接口。

Binder是何物呢?

Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by Binder.This class is an implementation of IBinder that provides standard local implementation of such an object.

可以看出 Binder是一个远程对象,它实现了提供本地标准接口的 IBinder

Stub类代表着远程服务,而 Proxy代表着远程服务在本地的代理。

0x04 获取 Binder 对象

在客户端 MainActivity中,绑定远程服务之后,使用 IRemoteService.Stub.asInterface()方法获取到远程服务的 Binder对象。

/**
 * Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
 * generating a proxy if needed.
 */
public static net.angrycode.learnpro.IRemoteService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof net.angrycode.learnpro.IRemoteService))) {
        return ((net.angrycode.learnpro.IRemoteService) iin);
    }
    return new net.angrycode.learnpro.IRemoteService.Stub.Proxy(obj);
}

这个方法先查找本地是否存在这个对象,存在则返回;不存在则返回一个 Proxy对象。

通过定点调试,可以知道当 RemoteService在子进程中时, asInterface(obj)参数是一个 BinderProxy对象,这个是远程服务进程的代理类。这个时候返回给客户端的是 Proxy对象。

客户端与服务端不在同一进程时,通过 BinderProxy进行通信

当把 manifestRemoteServiceandroid:process=':remote'配置去掉时, asInterface(obj)的参数的传递就是 RemoteService$1,其实就是 RemoteService里面的内部类 Stub

然后我们再回到多进程的流程来,跳转到 Proxy

0x05 Proxy.transact()

通过名字知道 Proxy就是远程服务的代理,它持有 Binder的引用。当客户端调用 iRemoteService.getText()时其实是进入到 Proxy类中 getText()方法。

public java.lang.String getText() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.lang.String _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

首先获取到两个 Parcel对象,这个是进程间通信的数据结构。 _data_reply分别为 getText()需要传递的参数和返回值, getText()无需参数,只有 String类型返回值。

然后调用 mRemotetransact()方法(其实就是调用 BinderProxytransact()方法)。然后通过 _reply获取到执行方法后的返回值,这里就是一个 RemoteService里面实现的 String

Proxy中执行 transact()方法后又回调到哪里了呢?

onTransact()方法中设置一个断点,通过调试,我们发现其实是回调到了 Stub类中 onTransact()方法

0x06 Stub.onTransact()

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getText: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getText();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

onTransact()方法中第一个参数 code是与 transact()第一个参数 code是对应的,这是客户端与服务端约定好的常量。

这时候会执行到 onTransact()方法中的 _result=this.getText()方法。而 Stub类是在 RemoteService中实现的,故就访问到远程服务中资源了。

0x07 总结

通过以上流程分析可以知道,通过 bindService绑定一个服务之后在 onServiceConnected()中拿到了远程服务的在本地的 Proxy,通过它与远程服务进行通信。

原文发布于微信公众号 - 终身开发者(AngryCode)

原文发表时间:2019-05-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券