AIDL学习

目录

一、项目准备

[1. 创建主项目(服务端 + 客户端可在同一项目,也可分开,这里以单项目多模块为例)](#1. 创建主项目(服务端 + 客户端可在同一项目,也可分开,这里以单项目多模块为例))

[二、创建 AIDL 接口文件(核心步骤)](#二、创建 AIDL 接口文件(核心步骤))

[1. 创建 AIDL 目录和文件](#1. 创建 AIDL 目录和文件)

[2. 编写 AIDL 接口内容](#2. 编写 AIDL 接口内容)

[3. 同步 AIDL 文件](#3. 同步 AIDL 文件)

[三、实现服务端 Service](#三、实现服务端 Service)

[1. 创建 Service 类](#1. 创建 Service 类)

[2. 在 Manifest 中注册 Service(关键:指定独立进程)](#2. 在 Manifest 中注册 Service(关键:指定独立进程))

[四、编写客户端 Activity(绑定服务并通信)](#四、编写客户端 Activity(绑定服务并通信))

[1. 创建布局文件](#1. 创建布局文件)

[2. 编写客户端 Activity 代码](#2. 编写客户端 Activity 代码)

五、运行与验证

[1. 运行项目](#1. 运行项目)

[2. 操作流程与预期效果](#2. 操作流程与预期效果)

六、核心流程总结

文件路径总览

项目结构与文件作用

代码执行流程(时序图)

核心流程拆解(分阶段详解)

[阶段 1:AIDL 接口定义与生成](#阶段 1:AIDL 接口定义与生成)

[阶段 2:服务端 Service 实现](#阶段 2:服务端 Service 实现)

[阶段 3:客户端绑定服务(跨进程连接建立)](#阶段 3:客户端绑定服务(跨进程连接建立))

[阶段 4:跨进程方法调用(核心交互)](#阶段 4:跨进程方法调用(核心交互))

[阶段 5:服务销毁(可选)](#阶段 5:服务销毁(可选))

[跨进程通信原理(Binder 机制)](#跨进程通信原理(Binder 机制))

关键细节与注意事项


一、项目准备

1. 创建主项目(服务端 + 客户端可在同一项目,也可分开,这里以单项目多模块为例)
  • 打开 Android Studio,新建 Empty Activity 项目,命名为 AIDLDemo,包名默认 com.example.aidldemo,语言选 Java
  • 项目结构采用 单应用内跨进程(通过不同进程启动服务),无需创建多模块。

二、创建 AIDL 接口文件(核心步骤)

AIDL 文件是跨进程通信的 "协议",服务端和客户端需完全一致(包名、内容)。

1. 创建 AIDL 目录和文件
  • 路径 :在 app/src/main 目录右键 → NewFolderAIDL Folder,默认路径即可(自动生成 aidl 目录)。
  • aidl 目录下创建与 Java 代码相同的包名(保持包结构一致):
    右键 aidlNewPackage,输入 com.example.aidldemo
  • 在该包下创建 AIDL 接口文件:
    右键包名 → NewAIDL File,命名为 IMyAidlInterface.aidl
2. 编写 AIDL 接口内容

文件路径app/src/main/aidl/com/example/aidldemo/IMyAidlInterface.aidl

复制代码
// IMyAidlInterface.aidl
package com.example.aidldemo;

// 定义跨进程调用的接口方法
interface IMyAidlInterface {
    // 示例:加法计算
    int add(int a, int b);
    // 示例:获取服务端信息
    String getServiceInfo();
}
3. 同步 AIDL 文件

点击菜单栏 BuildMake Project ,系统会自动在 app/build/generated/aidl_source_output_dir 目录生成对应的 Java 接口文件(无需手动修改)。

三、实现服务端 Service

服务端需创建 Service 并实现 AIDL 接口,通过 onBind 返回接口实例。

1. 创建 Service 类

文件路径app/src/main/java/com/example/aidldemo/MyAidlService.java

复制代码
package com.example.aidldemo;

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

// 服务端 Service,实现 AIDL 接口
public class MyAidlService extends Service {
    private static final String TAG = "MyAidlService";

    // 创建 AIDL 接口的实现类(Stub 是自动生成的抽象类)
    private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public int add(int a, int b) throws RemoteException {
            Log.d(TAG, "客户端调用 add 方法:a=" + a + ", b=" + b);
            return a + b; // 实现加法逻辑
        }

        @Override
        public String getServiceInfo() throws RemoteException {
            Log.d(TAG, "客户端调用 getServiceInfo 方法");
            return "这是来自服务端的消息:AIDL 通信成功!";
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "Service 已创建");
    }

    // 返回 AIDL 接口实例,供客户端绑定
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "客户端绑定服务");
        return mBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Service 已销毁");
    }
}
2. 在 Manifest 中注册 Service(关键:指定独立进程)

文件路径app/src/main/AndroidManifest.xml

<application> 标签内添加 Service 声明,并通过 android:process 指定独立进程(模拟跨进程场景):

复制代码
<service
    android:name=".MyAidlService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote"> <!-- 关键:指定独立进程,名称任意,以冒号开头表示私有进程 -->
    <intent-filter>
        <action android:name="com.example.aidldemo.AIDL_SERVICE" /> <!-- 自定义 Action,供客户端绑定 -->
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

四、编写客户端 Activity(绑定服务并通信)

客户端需绑定服务端 Service,通过 AIDL 接口调用服务端方法。

1. 创建布局文件

文件路径app/src/main/res/layout/activity_main.xml

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp"
    android:gravity="center_horizontal">

    <Button
        android:id="@+id/btn_bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务" />

    <Button
        android:id="@+id/btn_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="调用加法方法"
        android:layout_marginTop="20dp" />

    <Button
        android:id="@+id/btn_get_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="获取服务信息"
        android:layout_marginTop="20dp" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:textSize="16sp" />
</LinearLayout>
2. 编写客户端 Activity 代码

文件路径app/src/main/java/com/example/aidldemo/MainActivity.java

复制代码
package com.example.aidldemo;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

// 客户端 Activity,绑定服务并通过 AIDL 通信
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private IMyAidlInterface mAidlInterface; // AIDL 接口实例
    private boolean isBound = false; // 服务是否已绑定
    private TextView tvResult;

    // 服务连接回调,监控绑定状态
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 绑定成功:通过 IBinder 获取 AIDL 接口实例
            mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            isBound = true;
            Log.d(TAG, "服务绑定成功");
            tvResult.setText("服务绑定成功,可调用方法");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 服务意外断开(如崩溃)
            mAidlInterface = null;
            isBound = false;
            Log.d(TAG, "服务断开连接");
            tvResult.setText("服务已断开");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvResult = findViewById(R.id.tv_result);

        // 绑定服务按钮
        findViewById(R.id.btn_bind).setOnClickListener(v -> bindToService());

        // 调用加法方法按钮
        findViewById(R.id.btn_add).setOnClickListener(v -> callAddMethod());

        // 获取服务信息按钮
        findViewById(R.id.btn_get_info).setOnClickListener(v -> callGetInfoMethod());
    }

    // 绑定服务端 Service
    private void bindToService() {
        if (isBound) {
            Toast.makeText(this, "服务已绑定", Toast.LENGTH_SHORT).show();
            return;
        }
        Intent intent = new Intent();
        intent.setAction("com.example.aidldemo.AIDL_SERVICE"); // 与 Manifest 中声明的 Action 一致
        intent.setPackage(getPackageName()); // 指定服务端包名(当前应用包名)
        // 绑定服务:BIND_AUTO_CREATE 表示若服务未启动则自动创建
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    // 调用服务端的 add 方法
    private void callAddMethod() {
        if (!isBound || mAidlInterface == null) {
            Toast.makeText(this, "请先绑定服务", Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            int result = mAidlInterface.add(100, 200); // 跨进程调用加法
            String text = "100 + 200 = " + result;
            tvResult.setText(text);
            Log.d(TAG, text);
        } catch (RemoteException e) {
            e.printStackTrace();
            tvResult.setText("调用 add 方法失败:" + e.getMessage());
        }
    }

    // 调用服务端的 getServiceInfo 方法
    private void callGetInfoMethod() {
        if (!isBound || mAidlInterface == null) {
            Toast.makeText(this, "请先绑定服务", Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            String info = mAidlInterface.getServiceInfo(); // 跨进程调用获取信息
            tvResult.setText(info);
            Log.d(TAG, "服务信息:" + info);
        } catch (RemoteException e) {
            e.printStackTrace();
            tvResult.setText("调用 getServiceInfo 方法失败:" + e.getMessage());
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解绑服务,避免内存泄漏
        if (isBound) {
            unbindService(mServiceConnection);
            isBound = false;
            Log.d(TAG, "服务已解绑");
        }
    }
}

五、运行与验证

1. 运行项目

点击 Android Studio 运行按钮,将应用安装到模拟器或真机。

2. 操作流程与预期效果
步骤 操作 预期结果
1 点击 绑定服务 日志显示 服务绑定成功,文本框显示 "服务绑定成功,可调用方法"
2 点击 调用加法方法 文本框显示 100 + 200 = 300,服务端日志显示 客户端调用 add 方法
3 点击 获取服务信息 文本框显示 这是来自服务端的消息:AIDL 通信成功!,服务端日志显示 客户端调用 getServiceInfo 方法

六、核心流程总结

  1. AIDL 接口定义 :通过 .aidl 文件声明跨进程方法,系统自动生成 Java 接口(含 Stub 内部类)。
  2. 服务端实现Service 中创建 Stub 子类,实现 AIDL 方法,通过 onBind 返回 IBinder
  3. 客户端绑定 :通过 bindService 绑定服务,在 onServiceConnected 中通过 Stub.asInterface 获取 AIDL 接口实例。
  4. 跨进程通信:客户端调用 AIDL 接口方法,底层通过 Binder 机制实现进程间数据传输,服务端处理后返回结果。

文件路径总览

复制代码
app
└── src
    └── main
        ├── aidl
        │   └── com
        │       └── example
        │           └── aidldemo
        │               └── IMyAidlInterface.aidl  // AIDL 接口文件
        ├── java
        │   └── com
        │       └── example
        │           └── aidldemo
        │               ├── MainActivity.java      // 客户端 Activity
        │               └── MyAidlService.java     // 服务端 Service
        ├── AndroidManifest.xml                    // 注册 Service 并指定进程
        └── res
            └── layout
                └── activity_main.xml              // 客户端布局

项目结构与文件作用

先理清项目关键文件的职责,后续流程会围绕这些文件展开:

文件路径 类型 作用
app/src/main/aidl/com/example/aidldemo/IMyAidlInterface.aidl AIDL 接口 定义跨进程通信的方法(如 addgetServiceInfo),是通信 "协议"
app/src/main/java/com/example/aidldemo/MyAidlService.java Service 服务端实现,通过 Stub 子类实现 AIDL 方法,供客户端绑定
app/src/main/java/com/example/aidldemo/MainActivity.java Activity 客户端界面,负责绑定服务、调用 AIDL 方法
app/src/main/AndroidManifest.xml 清单文件 注册 MyAidlService,并指定独立进程(模拟跨进程)
app/src/main/res/layout/activity_main.xml 布局文件 客户端 UI,包含绑定、调用按钮和结果显示文本

代码执行流程(时序图)

应用启动 → 绑定服务 → 跨进程调用 → 服务销毁 ,完整时序如下:

核心流程拆解(分阶段详解)

阶段 1:AIDL 接口定义与生成

  1. 编写 AIDL 文件
    IMyAidlInterface.aidl 中声明跨进程方法(addgetServiceInfo),这是通信的 "契约"。
  2. 自动生成 Java 接口
    执行 Make Project 后,系统自动生成 IMyAidlInterface.java ,包含:
    • Stub 抽象类(服务端需继承实现,客户端通过它获取接口实例)。
    • asInterface(IBinder) 方法(客户端用它将 IBinder 转换为 AIDL 接口)。

阶段 2:服务端 Service 实现

  1. 继承 Stub 并实现方法
    MyAidlService 中创建 Stub 子类,重写 addgetServiceInfo 方法,实现业务逻辑。
  2. 通过 onBind 返回 IBinder
    客户端绑定服务时,onBind 返回 StubIBinder,作为跨进程通信的 "通道"。

阶段 3:客户端绑定服务(跨进程连接建立)

  1. 发起绑定请求
    客户端调用 bindService,传入 Intent(指定 Service 的 Action 和包名)。
  2. 系统启动 Service
    若 Service 未启动,系统先调用 MyAidlService.onCreate() 创建服务。
  3. 返回 IBinder 并建立连接
    服务端 onBind 返回 StubIBinder,系统通过 Binder 机制传递给客户端。
  4. 客户端获取 AIDL 接口实例
    客户端在 onServiceConnected 中,通过 Stub.asInterface(IBinder) 获取 AIDL 接口实例,后续可直接调用接口方法。

阶段 4:跨进程方法调用(核心交互)

add(100, 200) 为例,跨进程调用流程:

  1. 客户端调用 AIDL 方法
    mAidlInterface.add(100, 200) 触发跨进程调用。
  2. Binder 机制传输参数
    系统将参数(100, 200)通过 Binder 驱动,从客户端进程传输到服务端进程。
  3. 服务端执行逻辑并返回结果
    服务端 Stubadd 方法被调用,计算结果(300)后,再通过 Binder 机制传回客户端。
  4. 客户端接收结果并更新 UI
    客户端拿到结果(300),更新文本框显示。

阶段 5:服务销毁(可选)

  • 手动解绑 :客户端 onDestroy 中调用 unbindService,触发服务端 onUnbind(若不再有其他绑定,会调用 onDestroy 销毁 Service)。
  • 系统回收 :若 Service 是 startService 启动的,需手动 stopServicestopSelf 销毁。

跨进程通信原理(Binder 机制)

AIDL 底层依赖 Android Binder 机制 实现跨进程通信,核心流程:

  1. 进程隔离:Android 中不同应用 / 模块运行在独立进程,内存不共享。
  2. Binder 驱动:作为进程间通信的 "桥梁",负责传递数据、转发调用。
  3. Proxy/Stub 代理
    • 客户端通过 Stub.asInterface 获取 Proxy 代理,调用方法时,Proxy 将参数打包,通过 Binder 发送给服务端。
    • 服务端 Stub 接收请求,解包参数并执行实际方法,再将结果打包返回。

关键细节与注意事项

  1. AIDL 数据类型限制
    仅支持基本类型、StringParcelableList(元素需是 AIDL 支持类型)、Map(键值需是 AIDL 支持类型)。
  2. 进程安全
    服务端 Stub 的方法运行在服务端进程,需注意线程安全(默认在主线程,耗时操作需开线程)。
  3. 绑定状态管理
    客户端需在 onDestroy 中解绑服务,避免内存泄漏。
  4. 异常处理
    跨进程调用可能因服务端崩溃、进程被杀等失败,需捕获 RemoteException
相关推荐
牧天白衣.35 分钟前
区块链基础之Merkle B+树
学习·区块链
醉卧红尘的鱼2 小时前
蒙特卡罗方法(Monte Carlo Method)_学习笔记
学习·算法
wyn200011283 小时前
Hive学习笔记
hive·笔记·学习
人工智能转人机3 小时前
16day-人工智能-机器学习-特征工程
人工智能·学习·机器学习·特征工程
努力自学的小夏3 小时前
RK3568 Linux驱动学习——字符设备驱动开发
linux·驱动开发·笔记·学习
艾莉丝努力练剑3 小时前
【数据结构与算法】顺序表和链表、栈和队列、二叉树、排序等数据结构的完整代码收录
c语言·数据结构·学习·链表
老虎06274 小时前
JavaWeb(苍穹外卖)--学习笔记17(Websocket)
笔记·websocket·学习
★YUI★5 小时前
学习游戏制作记录(将各种属性应用于战斗以及实体的死亡)8.5
学习·游戏·unity·c#
yiqiqukanhaiba6 小时前
江协科技STM32学习笔记1
科技·stm32·学习