AIDL接口的使用和jar包的编译

705 阅读5分钟

发现网上这类文章比较少,之前自己做的时候遇到了很多问题,现在就新版本的as(2021.1.1 patch3)做一个从开发到提供jar包的全流程教程,欢迎转载,转载前请留言。

一.什么是AIDL

参考官方文章 Android 接口定义语言 (AIDL) developer.android.google.cn/guide/compo…

简单讲就是c/s模式的跨进程通信手段,一般用于framework中的java service与其他进程通信的手段,也会用于app开发中,向其他app提供接口。

二.为什么使用AIDL

和其他跨进程通信方式之间的优缺点

名称优点缺点适用场景
Bundle简单易用只能传输Bundle支持的数据类型四大组件间的进程通信
文件共享简单易用不适合高并发场景,无法及时通信无高并发访问,交换简单数据,实时性不高
AIDL功能强大,支持一对多并发通信,支持及时通信使用复杂,需要处理线程同步一对多且有远程调用需求
Messenger功能一般,支持一对多串行,支持实时通信不能很好处理高并发,不支持远程调用,数据只能使用Message传输低并发的一对多即时通信,无须直接返回结果,无远程调用
ContentProvider数据访问方面功能强大,支持一对多并发数据共享受约束的AIDL,主要提供数据的增删改查一对多进程间数据共享
Socket功能强大,支持一对多并发实时通信实现复杂,不支持直接的远程调用网络数据交互
注:该表格信息来自www.jianshu.com/p/cb8fa31a4…

三.使用的方式

关于创建接口,传递数据的方式,第一章中的官方文档讲的非常详细了,我这里做一个整体项目的详细讲解。 在as中,从创建项目,到交付jar包的整个流程

1.创建AIDL项目(项目架构简介和操作步骤)

(1)和普通项目一样,首先new 一个project

new project.png

(2)在本例中,我们在同一个project中建立两个app和一个module

顺序不能变,从上到下有依赖 1.new一个module,作为AIDL库 new module.png new aidl module.png new aidl文件.png

新建两个aidl文件,一个作为callback,一个作为service module详细结构 aidl module结构.png

2.new一个app,作为服务端 new module.png 选phone&table.png 新建一个activity(占位用的,不然安装不了)和一个service(核心类,是远程服务端) aidlservice.png 3.默认的app,用来做客户端 app总览如下图 app总览.png 如下图,一个activity 包含按钮: ①绑定/解绑连接

②调用远程方法

③加入/离开客户端列表(列表由远程服务维护)

④获取客户端列表,注册/解注册回调

UI如下 按钮.png

2.完善AIDL文件

// IParticipateCallback.aidl
package com.mingtianyeshihaotianqi.aidlservicelib;

interface IParticipateCallback {
    // 用户加入或者离开的回调
    void onParticipate(String name, boolean joinOrLeave);
}

里面的方法对应五种操作

// IRemoteService.aidl
package com.mingtianyeshihaotianqi.aidlservicelib;

import com.mingtianyeshihaotianqi.aidlservicelib.IParticipateCallback;

interface IRemoteService {
    int someOperate(int a, int b);

    void join(IBinder token, String name);
    void leave(IBinder token);
    List<String> getParticipators();

    void registerParticipateCallback(IParticipateCallback cb);
    void unregisterParticipateCallback(IParticipateCallback cb);
}

写完记得make一下,生成中间java类 make module.png

3.完善remote service

首先继承AIDL编译出的中间类IRemoteService.Stub,并覆写里面的方法,在对应的方法中实现相应的逻辑。

    private RemoteCallbackList<IParticipateCallback> mCallbacks = new RemoteCallbackList<>();//维护一个客户端回调列表

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {//实现一个stub,这里的方法会被客户端调用。
        ...
        @Override
        public void registerParticipateCallback(IParticipateCallback cb) throws RemoteException {
            mCallbacks.register(cb);//在这里实现注册回调的逻辑
        }

        @Override
        public void unregisterParticipateCallback(IParticipateCallback cb) throws RemoteException {
            mCallbacks.unregister(cb);
        }

        @Override
        public void join(IBinder token, String name) throws RemoteException {
            ...
            // 通知client加入
            notifyParticipate(client.mName, true);
        }

        @Override
        public void leave(IBinder token) throws RemoteException {
            ...
            // 通知client离开
            notifyParticipate(client.mName, false);
        }
    };

一个小细节,在销毁service时释放所有的回调

    @Override
    public void onDestroy() {
        super.onDestroy();

        // 取消掉所有的回调
        mCallbacks.kill();
    }

4.完善客户端app

首先导入aidl依赖

project structure.png

add module2.png add module3.png

继承连接匿名内部类

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Toast.makeText(MainActivity.this, "Service connected", Toast.LENGTH_SHORT).show();

            mService = IRemoteService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(MainActivity.this, "Service disconnected", Toast.LENGTH_SHORT).show();

            mService = null;
        }
    };

实现回调接口,监听remote service的变化

    private IParticipateCallback mParticipateCallback = new IParticipateCallback.Stub() {

        @Override
        public void onParticipate(String name, boolean joinOrLeave) throws RemoteException {
            if (joinOrLeave) {
                mAdapter.add(name);
            } else {
                mAdapter.remove(name);
            }
        }
    };

下面是官网中的启动方法

Intent intent = new Intent(Binding.this, RemoteService.class);
            intent.setAction(IRemoteService.class.getName());
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
            intent.setAction(ISecondary.class.getName());
            bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE);
            isBound = true;
            callbackText.setText("Binding.");

但是请注意,在android11系统中,对读取使用其他应用包名做了限制,所以需要在清单文件中增加对应的包名请求,不然会报错找不到服务

<queries>
        <package android:name="com.mingtianyeshihaotianqi.aidlservice"/>
    </queries>

5.使用manager类在jar包中提供绑定逻辑

这里其实是把之前导入module依赖,转换成楼使用jar包依赖,一般开发出的aidl接口,多以jar包形式提供给其他同事使用,所以编译jar包也是必须的技能

四.注意事项

1.处理客户端异常退出

在remoteService中继承IBinder.DeathRecipient,并覆写binderDied,在此方法中处理异常。

    private final class Client implements IBinder.DeathRecipient {
        public final IBinder mToken;
        public final String mName;

        public Client(IBinder token, String name) {
            mToken = token;
            mName = name;
        }

        @Override
        public void binderDied() {
            // 客户端死掉,执行此回调
            int index = mClients.indexOf(this);
            if (index < 0) {
                return;
            }

            Log.d(TAG, "client died: " + mName);
            mClients.remove(this);

            // 通知client离开
            notifyParticipate(mName, false);
        }
    }

2.处理服务端异常退出

调用您在接口上定义的方法。您应始终捕获 [DeadObjectException](https://developer.android.google.cn/reference/android/os/DeadObjectException?hl=zh-cn) 异常,系统会在连接中断时抛出此异常。您还应捕获 [SecurityException](https://developer.android.google.cn/reference/java/lang/SecurityException?hl=zh-cn) 异常,当 IPC 方法调用中两个进程的 AIDL 定义发生冲突时,系统会抛出此异常。

参考文章 Android Service实现双向通信 www.race604.com/communicate… www.race604.com/communicate… www.race604.com/communicate… 远程Service中的DeathRecipient和RemoteCallbackList www.jianshu.com/p/5a2fd8946… Android DeadObjectException 异常 aidl通信 blog.csdn.net/jqwei2/arti…