aidl 跨进程通信流程

新建一个工程,然后想新建aidl文件,src/main 右键 → New → AIDL, 弹出的窗口是灰色的,提示 AIDL file(requires setting the buildfeatures.aidl to true in the Gradle build files)

根据提示,在build.gradle.kts中开启aidl支持:

同步之后就可以新建aidl文件了,还是这个步骤,点击aidl菜单。

生成了一个模板aidl文件:

随意定义点接口:

然后编译下,自动生成了IMyAidlInterface.java文件,如下:

文件内容如下:

java 复制代码
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Using: C:\\Users\\86186\\AppData\\Local\\Android\\Sdk\\build-tools\\36.0.0\\aidl.exe -pC:\\Users\\86186\\AppData\\Local\\Android\\Sdk\\platforms\\android-36\\framework.aidl -oE:\\android\\projects\\binderdemo\\app\\build\\generated\\aidl_source_output_dir\\debug\\out -IE:\\android\\projects\\binderdemo\\app\\src\\main\\aidl -IE:\\android\\projects\\binderdemo\\app\\src\\debug\\aidl -IC:\\Users\\86186\\.gradle\\caches\\9.1.0\\transforms\\126ef151667daa32756d4a8a1016b441\\workspace\\transformed\\core-1.18.0\\aidl -IC:\\Users\\86186\\.gradle\\caches\\9.1.0\\transforms\\b4877cb6e78f8cd2f0c4778997dbc841\\workspace\\transformed\\versionedparcelable-1.1.1\\aidl -dC:\\Users\\86186\\AppData\\Local\\Temp\\aidl5851206668669868321.d E:\\android\\projects\\binderdemo\\app\\src\\main\\aidl\\com\\example\\binderdemo\\IMyAidlInterface.aidl
 *
 * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..).
 * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER
 * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE.
 */
package com.example.binderdemo;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
  /** Default implementation for IMyAidlInterface. */
  public static class Default implements com.example.binderdemo.IMyAidlInterface
  {
    @Override public int add(int a, int b) throws android.os.RemoteException
    {
      return 0;
    }
    @Override public java.lang.String getMessage() throws android.os.RemoteException
    {
      return null;
    }
    @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.example.binderdemo.IMyAidlInterface
  {
    /** Construct the stub and attach it to the interface. */
    @SuppressWarnings("this-escape")
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.binderdemo.IMyAidlInterface interface,
     * generating a proxy if needed.
     */
    public static com.example.binderdemo.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.binderdemo.IMyAidlInterface))) {
        return ((com.example.binderdemo.IMyAidlInterface)iin);
      }
      return new com.example.binderdemo.IMyAidlInterface.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;
      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;
      }
      switch (code)
      {
        case TRANSACTION_add:
        {
          int _arg0;
          _arg0 = data.readInt();
          int _arg1;
          _arg1 = data.readInt();
          int _result = this.add(_arg0, _arg1);
          reply.writeNoException();
          reply.writeInt(_result);
          break;
        }
        case TRANSACTION_getMessage:
        {
          java.lang.String _result = this.getMessage();
          reply.writeNoException();
          reply.writeString(_result);
          break;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
      return true;
    }
    private static class Proxy implements com.example.binderdemo.IMyAidlInterface
    {
      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 int add(int a, int b) 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);
          _data.writeInt(a);
          _data.writeInt(b);
          boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public java.lang.String getMessage() 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);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getMessage, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
    }
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  }
  /** @hide */
  public static final java.lang.String DESCRIPTOR = "com.example.binderdemo.IMyAidlInterface";
  public int add(int a, int b) throws android.os.RemoteException;
  public java.lang.String getMessage() throws android.os.RemoteException;
}

新建RemoteService, 如下:

详细代码如下:

java 复制代码
package com.example.binderdemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;

public class RemoteService extends Service {
    private static final String TAG = "RemoteService";

    private final IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
        @Override
        public int add(int a, int b) {
            Log.d(TAG, "add(" + a + "," + b + ") thread="
                    + Thread.currentThread().getName()
                    + ", pid=" + Process.myPid());
            return a + b;
        }

        @Override
        public String getMessage() {
            Log.d(TAG, "getMessage thread="
                    + Thread.currentThread().getName()
                    + ", pid=" + Process.myPid());
            return "Hello from pid=" + Process.myPid() + " , 为人不识陈近南,纵称英雄也枉然。";
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind thread="
                + Thread.currentThread().getName()
                + ", pid=" + Process.myPid());
        return binder;
    }
}

指定这个服务运行在子进程,名字随意:

MainActivity中调用这个服务:

java 复制代码
package com.example.binderdemo;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.widget.TextView;

import androidx.activity.ComponentActivity;
import androidx.annotation.Nullable;

public class MainActivity extends ComponentActivity {
    private static final String TAG = "ClientMainActivity";

    private IMyAidlInterface remote;
    private TextView tv;
    private boolean bound;

    private final ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected thread="
                    + Thread.currentThread().getName());
            // 这个service类型是android.os.BinderProxy,远端代理,用于跨进程通信。
            // asInterface返回一个客户端Proxy对象,里面的成员变量mRemote就是这个service对象。
            remote = IMyAidlInterface.Stub.asInterface(service);

            try {
                int result = remote.add(1, 2);
                String msg = remote.getMessage();

                Log.d(TAG, "add result=" + result + ", msg=" + msg);
                tv.setText("result=" + result + "\n" + msg);
                Log.d(TAG, "当前客户端进程:"+ Process.myPid());
                Log.d(TAG, "remote: " + remote);
                Log.d(TAG, "service: " + service);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
            remote = null;
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        tv = new TextView(this);
        setContentView(tv);

        Intent intent = new Intent(this, RemoteService.class);
        bound = bindService(intent, conn, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        if (bound) {
            unbindService(conn);
            bound = false;
        }
        super.onDestroy();
    }
}

运行:

看下日志:

ok. 由此看出,Client 的 pid 和 RemoteService 的 pid 不一样。onBind 在服务端主线程执行。

add() 和 getMessage() 不在主线程,而在 Binder:xxx_x 线程。客户端调用 remote.add(1, 2) 看起来像普通方法,实际跨进程了。

追踪下关键代码:

IMyAidlInterface.Stub.asInterface(service)。

这句代码生成客户端代理对象(即IMyAidlInterface.java里的Proxy对象),其中service参数(打印出来类型是android.os.BinderProxy),该参数后续会被用于跨进程调用服务端接口。下面会提到。

这个asInterface方法详细代码如下:

java 复制代码
public static com.example.binderdemo.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // 在当前进程中查询本地 IInterface 实现实例。跨进程则返回null
      if (((iin!=null)&&(iin instanceof com.example.binderdemo.IMyAidlInterface))) {
        return ((com.example.binderdemo.IMyAidlInterface)iin);
      }
      return new com.example.binderdemo.IMyAidlInterface.Stub.Proxy(obj);
    }

参数android.os.IBinder obj, 当跨进程通信时,obj类型为android.os.BinderProxy。asInterface最终返回的是Proxy对象。而service和客户端在同一个进程,这个obj就是Binder(即服务端的 Stub 本身);

需要理解下android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 这句代码,DESCRIPTOR 是接口的唯一标识符。

研究下原理,先打开IMyAidlInterface.java,里面的内部类Stub里面包含有个类Proxy,其中add方法是这样调用的:

java 复制代码
@Override public int add(int a, int b) 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);
          _data.writeInt(a);
          _data.writeInt(b);
          boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }

这个Proxy是客户端代理。它把参数写入Parcel对象中,然后调用 mRemote.transact()方法发起跨进程调用。然后再从 _reply 里读调用结果。其中mRemote就算刚才IMyAidlInterface.Stub.asInterface(service)这句代码的参数。

再看下Stub类的onTransact方法(即服务端service接收调用),有这个代码片段:

java 复制代码
switch (code)
      {
        case TRANSACTION_add:
        {
          int _arg0;
          _arg0 = data.readInt();
          int _arg1;
          _arg1 = data.readInt();
          int _result = this.add(_arg0, _arg1); // 即RemoteService里面内部类实现的那个add方法
          reply.writeNoException();
          reply.writeInt(_result);
          break;
        }

Stub 是服务端桩。Binder 线程收到调用后,进入 onTransact。它从 _data 读参数。

调用服务里的那个add()方法,再把结果写进 _reply。

ok. 这个是aidl跨进程通信大致调用链路。后面再继续研究下。

总结:

AIDL 就是帮忙生成一个 Proxy 和一个 Stub,不用手写 Parcel 的读写、不用手写 transact / onTransact 的分发。

Proxy 负责:打包参数 → transact → 解包结果

Stub 负责:解包参数 → 调用实现 → 打包结果

接口名 DESCRIPTOR 负责:校验调用双方是不是同一个接口

TRANSACTION_xxx 负责:告诉 Stub 这次调用的是哪个方法

相关推荐
mmsx3 天前
Android 模块化三件套:轻量组件化、宿主插件双形态、AIDL 隔离
android·网络·组件化·aidl
阿pin2 个月前
Android随笔-AIDL
android·开发语言·aidl
与水同流4 个月前
Android13 AIDL HAL服务实现Demo
android·hal·aidl
齊家治國平天下5 个月前
Android 14 AIDL HAL 使用指南-获取服务流程解析
android·hal·aidl·servicemanager·aidl hal·获取服务
齊家治國平天下5 个月前
Android 14 AIDL HAL 使用指南-注册服务流程解析
hal·aidl·android14·servicemanager·aidl hal
阿亮爱学代码5 个月前
Service
运维·服务器·service·aidl·跨进程通信
哈哈,柳暗花明5 个月前
Framework开发专题系列四:Binder基础
framework·binder·aidl·messenger·hidl·native binder·asyncchannel
小书房6 个月前
Android Binder机制
binder·aidl
henysugar9 个月前
Android studio编译aidl若干问题记录
android·ide·android studio·aidl