前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >源码分析——从AIDL的使用开始理解Binder进程间通信的流程

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

作者头像
阳仔
发布2019-07-31 15:51:28
9150
发布2019-07-31 15:51:28
举报
文章被收录于专栏:终身开发者

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

0x00 一个 AIDL 的例子

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

代码语言:javascript
复制
interface IRemoteService {
    String getText();
}

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

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

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

0x01 远程服务 RemoteService

代码语言:javascript
复制
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文件中注册远程服务,指定进程为私有进程

代码语言:javascript
复制
<service android:name=".RemoteService"
    android:process=":remote">
    <intent-filter>
        <action android:name="net.angrycode.RemoteService"/>
    </intent-filter>
</service>

0x02 本地客户端 MainActivity

代码语言:javascript
复制
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

代码语言:javascript
复制
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对象。

代码语言:javascript
复制
/**
 * 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()方法。

代码语言:javascript
复制
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()

代码语言:javascript
复制
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,通过它与远程服务进行通信。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 终身开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 一个 AIDL 的例子
  • 0x01 远程服务 RemoteService
  • 0x02 本地客户端 MainActivity
  • 0x03 IRemoteService 接口
  • 0x04 获取 Binder 对象
  • 0x05 Proxy.transact()
  • 0x06 Stub.onTransact()
  • 0x07 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档