Android AIDL 教程 (一)—— 简单的示例

AndroidStudio 引用 aidl 文件的两种方法

Android AIDL 教程 (一)—— 简单的示例

Android AIDL 传递对象(Parceable)

AIDL(Android Interface Definition Language)是Android系统自定义的接口描述语言。可以用来实现进程间的通讯。

在 Android 中,要实现进程间的通讯,一般来说,有以下几种方式:

  • 使用 Bundle

最常见的的是我们通过特定的 Action 或者 data 启动另外一个应用的 Activity 或者 service。我们可以将要传递的数据封装在 bundle 当中。

  • 文件共享

两个应用读取某个文件,从而达到进程通讯的问题,不过这种方法需要处理好文件锁的问题,不然很容易引发数据错乱。

  • 使用 Messenger

Messenger 进行进程间的通讯是串行的,而且是单向的,如果客户端和服务端想进行双向通讯,需要维护两个 Messenger,相对比较麻烦

  • 使用 AIDL

AIDL 简介

aidl 一般用来进程通讯。一般来说,主要有两种角色,客户端 (Client)和服务端(Server)。

服务端

一般用来处理客户端的请求,他把与客户端通讯的方式抽象成接口,并编写成 AIDL 文件。
通常服务端需要实现一个 Service,来处理客户端的请求

客户端

通常我们需要将服务端 的 AIDL 文件 copy 过来,并通过 Intent 的方式来启动我们服务端的 Service。

接下来让我们一起来实现一个简单功能,通过一哥 app(Clilent) 唤起另外一个 APP (Server),并进行两者之间的通讯。首先,我们先来看一下服务端的实现。


服务端的实现

服务端的实现,一般来说,需要以下步骤:

  • 将请求抽象成接口,并编写 aidl 文件;
  • 编写一个 Service,实现接口,处理客户端的请求,并将 binder 返回回去;
  • 在 AndroidManifet 配置 Service,将我们的 Service 暴露出去。

将请求抽象成接口,编写 aidl 文件

一般来说, AIDL 文件支持以下类型

  • Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等)
  • String
  • CharSequence
  • List
    List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 可选择将 List 用作“通用”类(例如,List)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
  • Map
    Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)。 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。
  • 实现 Parceable 的自定义类型

注意事项

  • 在 aidl 文件中,除了 Java 编程语言中的所有原语类型、String、CharSequence、List、Map,其他在 AIDL 文件中用到的类,你必须使用 import 语句导入,否则会报错。
  • 当你使用实现Parceable 的自定义类型的时候,当其作为参数的时候,你必须为其制定是输入或者是输出参数。
in 表示输入参数,即服务端可以修改该类型
out 表示输出参数,即客户端可以修改该类型,客户端不行
inout  表示客户端和服务端都可以修改该类型

void onSuccess(int code,in MusicInfo musicInfo);

有人可能会这样想,既然 inout 表示客户端和服务端都可以修改该类型,那我们平时在写 aidl 文件的时候,直接在方法参数前面加上 inout 修饰就 OK了,省得去区分。

这样做法当然不行,既然双方都可以修改,那系统的开销肯定会比较大。就好比管道一样。

说了这么多,接下来让我们一起来看一下例子 IEasyService.aidl

package xj.musicserver.easy;

// Declare any non-default types here with import statements

interface IEasyService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void connect(String mes);
    void disConnect(String mes);
}

这个 aidl 文件很接口,只有两个方法,connect 和 disConnect 方法。

在这里插入图片描述

这里我们把 aidl 文跟 Java 文件中放在一起,需要在 build.gradle 中配置

   sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
            aidl.srcDirs = ['src/main/java']
        }
    }

关于怎样在 AndroidStudio 中引用 aidl 文件的,可以参考我的这一篇文章 AndroidStudio 引用 aidl 文件的两种方法

编写一个 Service,实现接口,处理客户端的请求,并将接口返回回去

public class EasyService extends Service {

    private static final String TAG = "EasyService";
    public EasyService() {
    }

    IEasyService.Stub mIBinder=new IEasyService.Stub() {
        @Override
        public void connect(String mes) throws RemoteException {
            LogUtil.i(TAG,"connect:   mes =" + mes);

        }

        @Override
        public void disConnect(String mes) throws RemoteException {
            LogUtil.i(TAG, "disConnect:  mes =" +mes);
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        LogUtil.i(TAG,"onBind:   intent = "+intent.toString());
        return mIBinder;
    }



    @Override
    public boolean onUnbind(Intent intent) {
        LogUtil.i(TAG,"onUnbind:   =");
        return super.onUnbind(intent);

    }

    @Override
    public void onDestroy() {
        LogUtil.i(TAG,"onDestroy:   =");
        super.onDestroy();
    }
}

这个 Service 所做的动作就是当客户端连接上的时候,会启动我们的 Service,此时会调用 Service 的 onBInd 方法,在 onBinder 方法里面,我们将 mIBinder (实现了 IEasyService.Stub 接口)返回回去。他充当客户端和服务端的桥梁,通过他我们可以进行通讯。

至于这个 IEasyService.Stub 是什么呢?其实当我们在 AndroidStudio 里面编写完 aidl 文件,重新 make project 一下,就会自动生成了。

将服务端的 Service 暴露出去

在 AndroidManifest 文件下,配置 Service 的 action 及 exported 等信息。

其中 android:exported=“true” 表示别的进程可以访问,这个是必须配置的。android:process=":remote" 表示运行在 :remote 进程,不配置的话默认运行所在的 App 进程,这个可以不配置。

<service
    android:name=".easy.EasyService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">

    <intent-filter>
        <action android:name="xj.musicserver.easy.IEasyService"/>
    </intent-filter>
</service>



客户端的实现

  • 将服务端的 aidl 文件 copy 过来,注意要放在同一个包下
  • 通过服务端 Service 的 Action 启动, 当启动 Service 成功的时候,将服务端返回的 Binder 保存下来并转化成相应的实例。
  • 之后如果想与服务端通讯,通过保存下来的 Binder,即可调用服务端的方法。

第一步:将服务端的 aidl 文件 copy 过来,注意要放在同一个包下

在这里插入图片描述

  • 第二步,通过服务端 Service 的 Action 启动, 当启动 Service 成功的时候,将服务端返回的 Binder 保存下来并转化成相应的实例

当我们点击按钮的时候,我们通过 Action 去启动远程 servic。

case R.id.btn_start_service:
    LogUtil.i(TAG,"onButtonClick:   btn_start_service=");
    Intent intent = new Intent(ACTION);
	// 注意在 Android 5.0以后,不能通过隐式 Intent 启动 service,必须制定包名
    intent.setPackage(XJ_MUSICSERVER);
    bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);


private static final String ACTION = "xj.musicserver.easy.IEasyService";

private IEasyService mIEasyService;


ServiceConnection mServiceConnection=new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mIEasyService = IEasyService.Stub.asInterface(service);


    }

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



测试将会启动远程 Service,及服务端的 EasyService,并调用 onBind 方法,打印出相应信息。

xj.musicserver:remote I/MusicInterface: EasyService :onBind:   intent = Intent { act=xj.musicserver.easy.IEasyService pkg=xj.musicserver }

第三步:如果想与服务端通讯,通过保存下来的 Binder,即可调用服务端的方法。

比如当我们点击按钮的时候,调用 connect 方法。

case R.id.btn_contact:
    LogUtil.i(TAG,"onButtonClick:   btn_contact=");
    if(mIEasyService!=null){
        mIEasyService.connect(" Cilent connect");
    }


此时将会调用远程 service 的 mIbinder 的 connect 方法,log 中输出

xj.musicserver:remote I/MusicInterface: EasyService :connect:   mes = Cilent connect

注意事项

DEMO 下载地址


推荐阅读

一步步拆解 LeakCanary

Android 面试必备 - http 与 https 协议

Android 面试必备 - 计算机网络基本知识(TCP,UDP,Http,https)

Android 面试必备 - 线程

Android_interview github 地址

扫一扫,欢迎关注我的微信公众号 stormjun94, 目前专注于 Android 开发,主要分享 Android开发相关知识和技术人成长历程,包括个人总结,职场经验,面试经验等。

  • 9
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gdutxiaoxu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值