AIDL 初探

AIDL简介

​AIDL即Android IDL,是基于安卓平台的接口定义语言。AIDL的角色就是Android平台下的RPC。RPC是IPC的一个特例,能够跨进程远程访问。RPC的目的就是可以让程序不用担心方法具体是在哪个进程里面或者哪个机器上面,就像正常的本地方法那样去调用即可,RPC机制会处理所有的具体细节。

​AIDL提供Android平台的RPC的支持:开发者仅需要要定义AIDL,做一些相关的适配工作,然后就可以使用这些方法了,不用具体关心接口描述的方法究竟是在同一个进程中还是在其他的进程中。这些RPC实现的细节由Binder和系统来处理。

AIDL使用:以蓝牙扫描服务为例

​ 项目的Module下新建一个aidl文件,AS会自动为你生成一个包,文件会放在包里面,项目目录如下:

其中,app作为服务器端的Module,而bluetoorhtest作为客户端Module。

服务器端Module:

​ 定义AIDL文件。AIDL其实是一个接口文件,在里面定义相关的接口方法,而不作实现。

java 复制代码
// IBlueToothService.aidl
package com.lzy.bluetoothmanager;

// Declare any non-default types here with import statements
import com.lzy.bluetoothmanager.RmBluetoothDevice;

interface IBlueToothService {

    List<RmBluetoothDevice> getDevices();

}

这里注意传入参数的一些要点:

  • in表示从客户端传入,out表示从服务器传出,inout表示双向通信,即从客户端传入,并从服务器修改再传回。

  • 默认可以传入基本数据类型的参数,如果需要特殊参数,说明如下:

    1、传入对象类型,对象需要实现Parcelable接口,并在AIDL作如下定义,并且客户端和服务器端的module都要有一份内容完全相同的Model类副本。

    2、可以传入集合类型,如List,map等。

java 复制代码
// RmBluetoothDevice.aidl
package com.lzy.bluetoothmanager;

parcelable RmBluetoothDevice;

​ 在AS中Build服务器module。会生成对应的 Java 文件,一般不做修改,可查看其生成的Stub抽象类,该类继承于Binder,因此,预示着我们需要使用Service去做实现。

java 复制代码
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\Development\\Android\\projects-as\\BlueToothManager\\app\\src\\main\\aidl\\com\\lzy\\bluetoothmanager\\IBlueToothService.aidl
 */
package com.lzy.bluetoothmanager;
public interface IBlueToothService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.lzy.bluetoothmanager.IBlueToothService
{
private static final java.lang.String DESCRIPTOR = "com.lzy.bluetoothmanager.IBlueToothService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.lzy.bluetoothmanager.IBlueToothService interface,
 * generating a proxy if needed.
 */
public static com.lzy.bluetoothmanager.IBlueToothService asInterface(android.os.IBinder obj)
{
    ...
}
    ...
}

​ 新建一个Service类,定义内部类,实现AIDL的stub接口,并在onBinder方法中返回。

java 复制代码
/**
 * 蓝牙远程AIDL调用接口
 */
private class BlueToothAIDLBinder extends IBlueToothService.Stub{
    @Override
    public List<RmBluetoothDevice> getDevices() {
        if(mDiscoveryTask == null){
            mDeviceManager.clearAll();
            mDiscoveryTask = new BluetoothDiscoveryTask();
            mDiscoveryTask.execute();
        }
        return mDeviceManager.getAvailableDevices();
    }
}

​ 在AndroidManifest.xml中注册服务,并定义action。

xml 复制代码
<service android:name=".Services.BluetoothService">
    <intent-filter>
        <action android:name="lzy.service.bluetooth"/>
    </intent-filter>
</service>

客户端Module:

​ 拷贝一份服务器端Module的aidl文件到客户端,需要将整个包都拷贝进去,并保证aidl文件内容一致。然后Build客户端Module,生成对应Stub类的java文件。

​ 在Activity中定义内部类,实现ServiceConnection接口。实现接口中的 onServiceConnected 方法,该方法的第二个参数为IBinder类型,这就是远程服务器返回的Service对象,使用相关方法,将其化为对应aidl接口对象。

java 复制代码
/**
 * 连接远程蓝牙扫描服务
 */
private class BlueToothRemoteConnection implements ServiceConnection{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        isBtConnectionAlive = true;

        //获取远程传回的服务接口
        blueToothAidlBinder = IBlueToothService.Stub.asInterface(service);

        Thread btTask = new Thread(){
            @Override
            public void run() {
                // 定时获取蓝牙设备信息
                while(isBtConnectionAlive){
                    try {
                        Thread.sleep(500);
                        devices = blueToothAidlBinder.getDevices();
                        EventBusUtil.post(BlueToothConstant.EVENT_DEVICES_DESCOVERYING, null);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        btTask.start();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        unbindService(btConnection);
        isBtConnectionAlive = false;
    }
}

​ 最后使用Intent,绑定到指定action的Service。这样就实现了IPC通信。

java 复制代码
/**
 * 远程连接蓝牙服务器
 */
public void toConnectServer(){
    Intent intent = new Intent();
    intent.setAction(BlueToothConstant.BLUETOOTH_SERVICE_ACTION);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // Android 5.0 之后必须设置组件
        PackageManager pm = getApplication().getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(intent, 0);
        if (resolveInfo != null && resolveInfo.size() == 1) {
            ResolveInfo serviceInfo = resolveInfo.get(0);
            String packageName = serviceInfo.serviceInfo.packageName;
            String className = serviceInfo.serviceInfo.name;
            Intent componentIntent = new Intent(intent);
            componentIntent.setComponent(new ComponentName(packageName, className));
            bindService(componentIntent, btConnection, BIND_AUTO_CREATE);
        } else {
            Log.e(TAG, "BluetoothService is not installed.");
        }
    } else {
        bindService(intent, btConnection, BIND_AUTO_CREATE);
    }
}

**注意:**Android 5.0之后隐式声明Intent启动Service引发一些问题,会抛出异常,要求显式调用,但是不符合实际。因此,在Intent中使用Component来解决问题。

可以专门为此编写一个工具类方法,如下:

java 复制代码
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
    // Retrieve all services that can match the given intent
    PackageManager pm = context.getPackageManager();
    List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

    // Make sure only one match was found
    if (resolveInfo == null || resolveInfo.size() != 1) {
        return null;
    }

    // Get component info and create ComponentName
    ResolveInfo serviceInfo = resolveInfo.get(0);
    String packageName = serviceInfo.serviceInfo.packageName;
    String className = serviceInfo.serviceInfo.name;
    ComponentName component = new ComponentName(packageName, className);

    // Create a new intent. Use the old one for extras and such reuse
    Intent explicitIntent = new Intent(implicitIntent);

    // Set the component to be explicit
    explicitIntent.setComponent(component);

    return explicitIntent;
}

源代码

本Demo源码完全开源,实现AIDL简易蓝牙扫描功能,有学习兴趣的可以下载参考

Github链接:github.com/Miracle287/...

相关推荐
baidu_247438611 小时前
Android ViewModel定时任务
android·开发语言·javascript
有位神秘人2 小时前
Android中Notification的使用详解
android·java·javascript
·云扬·2 小时前
MySQL Binlog落盘机制深度解析:性能与安全性的平衡艺术
android·mysql·adb
独自破碎E3 小时前
【BISHI9】田忌赛马
android·java·开发语言
代码s贝多芬的音符4 小时前
android 两个人脸对比 mlkit
android
darkb1rd6 小时前
五、PHP类型转换与类型安全
android·安全·php
gjxDaniel6 小时前
Kotlin编程语言入门与常见问题
android·开发语言·kotlin
csj506 小时前
安卓基础之《(22)—高级控件(4)碎片Fragment》
android
峥嵘life7 小时前
Android16 【CTS】CtsMediaCodecTestCases等一些列Media测试存在Failed项
android·linux·学习
stevenzqzq8 小时前
Compose 中的状态可变性体系
android·compose