前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AIDL初步理解

AIDL初步理解

原创
作者头像
笔头
修改2024-05-25 07:46:41
3730
修改2024-05-25 07:46:41
举报
文章被收录于专栏:Android记忆

一、为什么使用AIDL

如果你是普通app开发,有可能遇不到跨进程访问数据。在车厂,智能座舱开发,跨进程访问数据是很平常的事,比如车机账户系统,很多模块都要获取当前车机账户信息,那账户系统就会提供相应数据给其他模块,账户系统和其他模块不属于同一个进程,这个时候就出现跨进程场景,Android系统为我们提供了一种技术方案:AIDL

二、AIDL使用

1.基本使用

服务端创建

服务端代码结构
服务端代码结构

AIDL文件

代码语言:javascript
复制
interface IDemoInterface {
    void demo();
    int getDemo();
    void setDemo(int i);
}

具体服务实现

代码语言:javascript
复制
public class DemoServer extends IDemoInterface.Stub{

    private int value=0;
    @Override
    public void demo() throws RemoteException {
        Log.e("DemoServer","demo");
    }

    @Override
    public int getDemo() throws RemoteException {
        return value;
    }

    @Override
    public void setDemo(int i) throws RemoteException {
        value=i;
        Log.e("DemoServer","setDemo"+i);
    }

}

服务

代码语言:javascript
复制
public class DemoService extends Service {
    private DemoServer demoServer;
    public DemoService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        demoServer=new DemoServer();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return demoServer;
    }
}

以上是服务端代码,基本功能就是写入值、取出值。

客户端代码

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {

    private TextView startService,demo,setDemo,getDemo;

    IDemoInterface idemo;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startService=findViewById(R.id.startService);
        demo=findViewById(R.id.demo);
        setDemo=findViewById(R.id.setDemo);
        getDemo=findViewById(R.id.getDemo);

        startService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction("com.roxmotor.aidlservice.DemoService");
                intent.setPackage("com.roxmotor.aidlservice");
                boolean result = getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
                Log.d("MainActivity",result+"");
            }
        });
        demo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    idemo.demo();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        setDemo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Log.d("MainActivity","设置了200");
                    idemo.setDemo(200);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        getDemo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    int value=idemo.getDemo();
                    Log.e("MainActivity","获取值:"+value);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //成功连接
            Log.d("MainActivity","pushManager ***************成功连接***************");
            idemo=IDemoInterface.Stub.asInterface(service);

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //断开连接调用
            Log.d("MainActivity","pushManager ***************连接已经断开***************");
        }
    };
}

执行结果

上面基本使用方式就不详细说了,这里就不详细说adil如何使用了,我们直接来看aidl是如何帮助我们跨进程处理数据的。

二、AIDL内部源码

代码语言:javascript
复制
public interface IDemoInterface extends android.os.IInterface
{
  /** Default implementation for IDemoInterface. */
  public static class Default implements com.roxmotor.aidlservice.IDemoInterface
  {
    @Override public void demo() throws android.os.RemoteException
    {
    }
    @Override public int getDemo() throws android.os.RemoteException
    {
      return 0;
    }
    @Override public void setDemo(int i) throws android.os.RemoteException
    {
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.roxmotor.aidlservice.IDemoInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.roxmotor.aidlservice.IDemoInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.roxmotor.aidlservice.IDemoInterface interface,
     * generating a proxy if needed.
     */
    public static com.roxmotor.aidlservice.IDemoInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.roxmotor.aidlservice.IDemoInterface))) {
        return ((com.roxmotor.aidlservice.IDemoInterface)iin);
      }
      return new com.roxmotor.aidlservice.IDemoInterface.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    @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_demo:
        {
          data.enforceInterface(descriptor);
          this.demo();
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_getDemo:
        {
          data.enforceInterface(descriptor);
          int _result = this.getDemo();
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        case TRANSACTION_setDemo:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          this.setDemo(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.roxmotor.aidlservice.IDemoInterface
    {
      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 void demo() 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_demo, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().demo();
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public int getDemo() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getDemo, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getDemo();
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public void setDemo(int i) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(i);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setDemo, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().setDemo(i);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static com.roxmotor.aidlservice.IDemoInterface sDefaultImpl;
    }
    static final int TRANSACTION_demo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getDemo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_setDemo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    public static boolean setDefaultImpl(com.roxmotor.aidlservice.IDemoInterface impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Stub.Proxy.sDefaultImpl != null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.roxmotor.aidlservice.IDemoInterface getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void demo() throws android.os.RemoteException;
  public int getDemo() throws android.os.RemoteException;
  public void setDemo(int i) throws android.os.RemoteException;
}

结构如下

三、AIDL理解

代码初看比较长,我们一个个来看。

1.我们从客户端开始看,客户端是如何拿到服务端句柄的,客户端和服务端是不同的进程,不同进程在不同的用户空间,不同用户空间,为了数据安全不能相互访问、修改数据。

看客户端代码,我们是通过idemo对象访问服务端接口的。idemo是怎么来的?

我们可以看到,我们是通过bindeService绑定DemoService ,绑定操作完成后会收到一个回调serviceConnection ,这个回调函数我们可以拿到一个binder,我们记住这个binder

我们再看下服务端Service,我们可以看到onBind函数里,返回了一个binder,这个binder就是Stub(DemoServer),回头来看,客户端绑定服务拿到的binder就是这个Stub

有没有发现,客户端里面有个IDemoInterface ,这个就涉及到客户端也定义了IDemoInterface.aidl文件,而且文件路径包名和服务端一样。这个是为什么?

我们先不关心这个包名为什么需要一样,假设可以不一样。

点击make project,会生成aidl类

我们再客户端代码可以看到idemo=IDemoInterface.Stub.asInterface(service);

我们进入asInterface可以看到我们拿到了proxy,(这个接口实现了IDemoInterface)

会看服务端定义了几个接口,我们需要无感访问服务端,那起码这个proxy也能提供一摸一样的接口吧,那我们在客户端定义的IDemoInterface是不是也要提供一样的接口,是的,必须一样,(也可以不一样,那aild类自己写也可以)。

我们在看看aidl类中proxy,remote对象,就是服务端中的binder,就是asInterface中的对象。

我们在看看proxy接口。每个接口中都有 _data.writeInterfaceToken(DESCRIPTOR);在调用remote的时候需要带入_data参数,这个参数里面设定了一个DESCRIPTOR,这个什么,应该是一个身份验证需要的数据。看代码,DESCRIPTOR

是aidl的包名+aidl名称,就是说在拿到服务端binder,调用服务端接口时,需要提供这个DESCRIPTOR,如果这个DESCRIPTOR和服务端不一样,那肯定校验不过(后面来看如何校验),所以包名和aidl名称必须和服务端一样。

现在我们知道了客户端aidl包名必须和服务端一样,我们继续看下proxy接口时具体如何访问服务端接口的。

代码语言:javascript
复制
boolean _status = mRemote.transact(Stub.TRANSACTION_demo, _data, _reply, 0);

拿到mRemote后,调用它的transact方法,执行binder内部操作(后面看transact如何执行)

这样就调用了服务端接口。

如果不用代理proxy也可以,自己收到写aidl类。

我们看下总结下客户端如何通过aidl访问服务端

1.Default

我们先看下Default这个类,他只是实现了com.roxmotor.aidlservice.IDemoInterface这个接口,而且都是空实现,同时并没有继承Binder,asBinder返回是null,我个人一般不用Default。

2.Stub

2.1 Stub()

Stub构造函数,当我们bindService时,在onBind返回一个IBind类型接口,因为所有的IBinder aidl 远程调用都要继承自IDemoInterface.Stub,所以必然会调用到

代码语言:javascript
复制
public Stub()
{
  this.attachInterface(this, DESCRIPTOR);
}

Stub 的空构造函数。而Stub 本身又是Binder 的子类。调用了Binder 的attachInterface 方法。

代码语言:javascript
复制
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}

分别保存了当前类的实例对象(This,也就是当前的binder)和当前类接口描述符(DESCRIPTOR),后面会通过DESCRIPTOR查找对应的binder

代码语言:javascript
复制
private static final java.lang.String DESCRIPTOR = "com.roxmotor.aidlservice.IDemoInterface";

2.2 asInterface

代码语言:javascript
复制
public static com.roxmotor.aidlservice.IDemoInterface asInterface(android.os.IBinder obj)
{
  if ((obj==null)) {
    return null;
  }
  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  if (((iin!=null)&&(iin instanceof com.roxmotor.aidlservice.IDemoInterface))) {
    return ((com.roxmotor.aidlservice.IDemoInterface)iin);
  }
  return new com.roxmotor.aidlservice.IDemoInterface.Stub.Proxy(obj);
}

我们知道这里的obj是bindService拿到的远程Binder,即BinderProxy(这里为什么是BinderProxy,后面文章会说到。)

那我们看下BinderProxy的queryLocalInterface(DESCRIPTOR)

代码语言:javascript
复制
public IInterface queryLocalInterface(String descriptor) {
    return null;
}

这里返回null,asInterface方法直接返回com.roxmotor.aidlservice.IDemoInterface.Stub.Proxy(obj)。如果obj不是远程的BinderProxy,那就是本进程Binder,其方法如下

代码语言:javascript
复制
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if (mDescriptor != null && mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

这里的mOwner,就是本地的一个Stub

这里的asInterface,就是判断是拿本地binder还是拿远程binder即BinderProxy

3.Proxy

代码语言:javascript
复制
private static class Proxy implements com.roxmotor.aidlservice.IDemoInterface
{
  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 void demo() 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_demo, _data, _reply, 0);
      if (!_status && getDefaultImpl() != null) {
        getDefaultImpl().demo();
        return;
      }
      _reply.readException();
    }
    finally {
      _reply.recycle();
      _data.recycle();
    }
  }
  @Override public int getDemo() throws android.os.RemoteException
  {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    int _result;
    try {
      _data.writeInterfaceToken(DESCRIPTOR);
      boolean _status = mRemote.transact(Stub.TRANSACTION_getDemo, _data, _reply, 0);
      if (!_status && getDefaultImpl() != null) {
        return getDefaultImpl().getDemo();
      }
      _reply.readException();
      _result = _reply.readInt();
    }
    finally {
      _reply.recycle();
      _data.recycle();
    }
    return _result;
  }
  @Override public void setDemo(int i) throws android.os.RemoteException
  {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
      _data.writeInterfaceToken(DESCRIPTOR);
      _data.writeInt(i);
      boolean _status = mRemote.transact(Stub.TRANSACTION_setDemo, _data, _reply, 0);
      if (!_status && getDefaultImpl() != null) {
        getDefaultImpl().setDemo(i);
        return;
      }
      _reply.readException();
    }
    finally {
      _reply.recycle();
      _data.recycle();
    }
  }
  public static com.roxmotor.aidlservice.IDemoInterface sDefaultImpl;
}

调用远程binder会走Proxy这里。

我们可以大概看看出,Proxy实现了IDemoInterface的方法,内部实现是通过mRemote去调用的。这个mRemote就是bindService拿到的IBinder service,也就是BinderProxy.

我们拿getDemo方法来看看

_data是传参,_reply是返回值,他们是Parcel类型(进程间通信由于资源不共享,因此无法直接传递对象,只能通过序列化在不同的空间拷贝两份相同的资源

我们可以看出,不管哪个方法,_data都要存储DESCRIPTOR,也就是描述符。现在我们知道为什么客户端adil为什么要和服务端一样了。他们一样的话,DESCRIPTOR也就一样,服务端可以通过这个DESCRIPTOR可以在onTransact里面找到对应的方法。

getDemo没有参数,所以_data里面没有存储给服务端参数。setDemo里面存储了一个参数,调用了_data.writeInt(i)。

接下来执行

代码语言:javascript
复制
mRemote.transact(Stub.TRANSACTION_getDemo, _data, _reply, 0);

这里的mRemote,我们现在知道,他是BinderProxy.调用了transact方法,最终还是执行了Stub中的onTransact

代码语言:javascript
复制
  @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_demo:
        {
          data.enforceInterface(descriptor);
          this.demo();
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_getDemo:
        {
          data.enforceInterface(descriptor);
          int _result = this.getDemo();
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        case TRANSACTION_setDemo:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          this.setDemo(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }

我们看下TRANSACTION_getDemo,这里我们把this.getDemo方法的结果存储到reply中。这里this.getDemo是那里实现呢?

这里的Stub实现了IDemoInterface接口,但是并没有真正的实现,那在那里实现了?我们回看代码,发现了一个叫DemoServer的类,继承了IDemoInterface.Stub,实现了所有方法。

代码语言:javascript
复制
@Override
public int getDemo() throws RemoteException {
    return value;
}

this.getDemo就是返回value。

我们继续往下看。

执行mRemote.transact方法后,_result = _reply.readInt();会取出_reply值,然后return _result。最终拿到返回值。

整个过程完成。

Proxy调用getDemo,实际上是通过调用mRemote.transact()来触发远端Stub的onTransact()

一般流程:

  1. 创建参数与返回值的Parcel对象,将参数写入Parcel。
  2. 调用mRemote.transact(),返回值会写入到Parcel对象中。
  3. 从Parcel对象中读出返回值并return。

四、总结

1.binderService,获得一个远程binder,即BinderProxy

2.在Proxy,拿到BinderProxy,同时通过Parcel将参数传给BinderProxy,最终触发BinderProxy的transact方法。

3.Binder的transact方法最终会触发到Server上Stub的onTransact方法。

4.Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将结果写入Parcel后传回。

5.Client的Ipc方法中,执行Binder的transact时,是阻塞等待的。一直到Server逻辑执行结束后才会继续执行。

6.当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。

大概流程如下:

五、问题

1.ServiceConnection是如何获取到Binder的而且是BinderProxy?

2.transact方法最终是如何触发Stub中的onTransact的?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、为什么使用AIDL
  • 二、AIDL使用
  • 二、AIDL内部源码
  • 三、AIDL理解
    • 1.Default
      • 2.Stub
        • 2.1 Stub()
        • 2.2 asInterface
      • 3.Proxy
      • 四、总结
      • 五、问题
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档