新建一个工程,然后想新建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 这次调用的是哪个方法