AIDL 接口的定义与生成,使用
wydaicls
2025-10-15 16:49
在 Android 开发中,​ asStub() 是 AIDL(Android Interface Definition Language)生成的接口类中的一个关键方法,主要用于将本地服务实现类转换为 Binder 的 Stub 对象(Binder 代理),从而支持跨进程通信(IPC)

一、AIDL 与 asStub() 的核心关系​
AIDL 的核心目标是定义跨进程通信的接口。编译器会根据 AIDL 文件生成以下关键类:

IYourInterface:用户定义的接口(继承自 IInterface)。
IYourInterface.Stub:服务端的基类(继承自 Binder,并实现 IYourInterface),负责处理客户端调用。
IYourInterface.Proxy:客户端的代理类,负责将方法调用封装为 Binder 事务。
而 asStub() 通常是 IYourInterface 接口中定义的方法(或由编译器生成的辅助方法),作用是将本地服务实现类(继承自 Stub)转换为 Binder 可识别的 Stub 对象,供服务端注册或客户端绑定。

二、asStub() 的典型使用场景​
假设你需要实现一个跨进程服务(如音乐播放服务),客户端通过 AIDL 接口调用服务端的方法(如播放、暂停)。此时 asStub() 的作用是将服务端的实现类暴露给 Binder 系统。

三、具体使用步骤与代码示例​
​1. 定义 AIDL 接口​
首先创建 AIDL 文件(如 IPlayerService.aidl),定义跨进程调用的方法:

java
复制代码
// IPlayerService.aidl
package com.example.music;

interface IPlayerService {
void play();
void pause();
String getCurrentSong();
}
编译器会自动生成 IPlayerService.java,其中包含 Stub 类和 Proxy 类。

​2. 实现服务端逻辑​
服务端需要实现 IPlayerService 接口(通常继承自 Stub 类),并完成具体业务逻辑:

java
复制代码
// PlayerService.java(服务端)
package com.example.music;

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

public class PlayerService extends Service {

// 服务端的 AIDL 接口实现类(继承自 Stub)
private final IPlayerService.Stub mBinder = new IPlayerService.Stub() {
    @Override
    public void play() throws RemoteException {
        // 实现播放逻辑
    }

    @Override
    public void pause() throws RemoteException {
        // 实现暂停逻辑
    }

    @Override
    public String getCurrentSong() throws RemoteException {
        return "Bohemian Rhapsody"; // 返回当前歌曲
    }
};

@Override
public IBinder onBind(Intent intent) {
    // 关键:通过 asStub() 或直接返回 mBinder(本质是 Stub 对象)
    // 注意:mBinder 已经是 IPlayerService.Stub 类型,无需额外调用 asStub()
    return mBinder;
}

}
​3. 客户端绑定服务并调用方法​

客户端通过 bindService 绑定服务,获取 IBinder 对象,并通过 asInterface() 将其转换为 IPlayerService 接口实例:

java
复制代码
// ClientActivity.java(客户端)
package com.example.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.appcompat.app.AppCompatActivity;
import com.example.music.IPlayerService;

public class ClientActivity extends AppCompatActivity {

private IPlayerService mPlayerService;

private final ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // 关键:将 Binder 对象转换为 AIDL 接口实例
        mPlayerService = IPlayerService.Stub.asInterface(service);
        
        try {
            mPlayerService.play(); // 调用远程方法
            String song = mPlayerService.getCurrentSong();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mPlayerService = null;
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_client);
    
    // 绑定服务
    Intent intent = new Intent(this, PlayerService.class);
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    unbindService(mConnection); // 解绑服务
}

}
四、关键细节解析​
asStub() 的本质​:
在 AIDL 生成的 IPlayerService 接口中,Stub.asInterface(service) 方法的作用是 将客户端获取的 IBinder 对象(服务端的 Binder 代理)转换为 IPlayerService 接口实例
,使得客户端可以像调用本地方法一样调用远程方法(底层通过 Binder 事务实现)。

​**服务端的 onBind()**​:

服务端通过 onBind() 返回 mBinder(继承自 Stub 的对象),该对象已经是 Binder 系统可识别的 Stub 类型。无需额外调用 asStub(),因为 mBinder 本身就是 Stub 的子类实例。

​跨进程调用的底层逻辑 ​:

当客户端调用 mPlayerService.play() 时,实际是通过 Proxy 类将方法调用封装为 Parcel 数据,通过 Binder 驱动发送到服务端;服务端的 Binder 线程池接收数据后,调用 Stub 类的对应方法执行逻辑,并将结果返回给客户端。

如果是在FW层使用呢

在 Android 框架层(FW 层,如 system_server 进程中的系统服务),AIDL 的 asStub()(或更准确地说,Stub.asInterface())的使用逻辑与应用层类似,但需要结合系统服务的特性(如权限控制、全局单例、与系统组件交互)进行调整。

一、FW 层使用 AIDL 的典型场景​
FW 层的系统服务(如 ActivityManagerService、PackageManagerService、WindowManagerService 等)需要通过跨进程通信(IPC)与其他进程(如应用进程、SurfaceFlinger 等)交互。AIDL 是实现这种 IPC 的核心工具。典型场景包括:

应用进程调用系统服务的方法(如 ActivityManagerService.startActivity())。
系统服务回调应用进程的监听器(如 PackageManagerService 通知应用包更新)。
二、FW 层 AIDL 接口的定义与生成​
​1. 定义 AIDL 接口文件​
AIDL 文件通常存放在 frameworks/base/aidl/ 目录下(如 frameworks/base/aidl/android/app/IActivityManager.aidl)。接口定义需符合系统服务的权限和功能需求:

java
复制代码
// IActivityManager.aidl(示例)
package android.app;

import android.content.Intent;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;

interface IActivityManager {
// 启动 Activity 的方法
int startActivity(in Intent intent, in String resolvedType, in IBinder resultTo,
in String resultWho, int requestCode, int flags,
in ProfilerInfo profilerInfo, in Bundle options) throws RemoteException;

// 注册 Activity 生命周期回调
void registerActivityLifecycleCallbacks(in IActivityLifecycleCallbacks callback) throws RemoteException;

// 其他系统级方法...

}
2. 编译器生成 Stub/Proxy 类​
AIDL 编译器会自动生成 IActivityManager.java,包含:

IActivityManager.Stub:系统服务端的基类(继承自 Binder,实现 IActivityManager 接口)。
IActivityManager.Proxy:客户端(应用进程)的代理类,封装 Binder 事务。
三、FW 层服务端的实现(系统服务)​​

系统服务(如 ActivityManagerService)需要继承 AIDL 生成的 Stub 类,并实现具体逻辑。以下是关键步骤:

​1. 实现 AIDL 接口的 Stub 类​
系统服务通常直接继承 IActivityManager.Stub,并在内部实现接口方法:

java
复制代码
// ActivityManagerService.java(简化版)
public final class ActivityManagerService extends IActivityManager.Stub {
// 单例模式(系统服务全局唯一)
private static final ActivityManagerService sInstance = new ActivityManagerService();

// 私有构造方法(仅系统进程可访问)
private ActivityManagerService() {
    // 初始化系统资源(如 Activity 栈、任务管理)
}

// 获取系统服务实例(供其他系统组件调用)
public static ActivityManagerService getInstance() {
    return sInstance;
}

// 实现 AIDL 接口方法:启动 Activity
@Override
public int startActivity(Intent intent, String resolvedType, IBinder resultTo,
                        String resultWho, int requestCode, int flags,
                        ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    // 实际启动 Activity 的逻辑(如创建 Activity 栈、触发生命周期回调)
    return ActivityStartController.startActivity(...);
}

// 实现 AIDL 接口方法:注册生命周期回调
@Override
public void registerActivityLifecycleCallbacks(IActivityLifecycleCallbacks callback)
        throws RemoteException {
    // 将回调接口存储到全局列表,供 Activity 生命周期事件触发时调用
    mLifecycleCallbacks.add(callback);
}

}
2. 暴露服务给 Binder 系统​
系统服务需要将自己注册到 ServiceManager(系统服务的注册中心),以便其他进程通过名称查找并绑定:

java
复制代码
// ActivityManagerService.java(在静态初始化块中注册)
static {
// 将自身注册到 ServiceManager,命名为 “activity”
ServiceManager.addService(“activity”, ActivityManagerService.getInstance());
}
四、FW 层客户端的调用(应用进程或其他系统服务)​​

客户端(如应用进程)通过 ServiceManager 获取系统服务的 IBinder 对象,并通过 Stub.asInterface() 转换为 AIDL 接口实例:

​1. 获取系统服务的 IBinder​
应用进程通过 ServiceManager.getService() 获取系统服务的 IBinder:

java
复制代码
// 应用进程中获取 ActivityManagerService 的 IBinder
IBinder activityManagerBinder = ServiceManager.getService(“activity”);
2. 转换为 AIDL 接口实例​
通过 Stub.asInterface() 将 IBinder 转换为 IActivityManager 接口,调用远程方法:

java
复制代码
// 转换为 IActivityManager 接口
IActivityManager am = IActivityManager.Stub.asInterface(activityManagerBinder);

try {
// 调用远程方法:启动 Activity
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
am.startActivity(intent, …);
} catch (RemoteException e) {
// 处理跨进程调用异常(如服务未注册、Binder 死亡)
e.printStackTrace();
}
后面再记录下…

欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目 Value
电脑 $1600
手机 $12
导管 $1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列 第二列 第三列
第一列文本居中 第二列文本居右 第三列文本居左

SmartyPants

SmartyPants 是一个文本转换工具,主要功能是将普通的 ASCII 标点符号自动转换为更美观的印刷体标点符号。例如:

原始符号 转换后 说明
"引号" “引号” 直引号变弯引号
'单引号' ‘单引号’ 直单引号变弯单引号
-- 两个连字符变短破折号
--- 三个连字符变长破折号
... 三个点变省略号

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

2014-01-07 2014-01-09 2014-01-11 2014-01-13 2014-01-15 2014-01-17 2014-01-19 2014-01-21 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML图表

可以使用UML图表进行渲染,例如下面产生的一个序列图:

王五 李四 张三 王五 李四 张三 李四想了很长时间, 文字太长了 不适合放在一行. 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 打量着王五... 很好... 王五, 你怎么样?
  • 关于 UML图表 语法,参考 这儿,

流程图

链接

长方形

圆角长方形

菱形

  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart.js的流程图语法:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐