AIDL简述

前言

AIDL,Android Interface Definition Language,Android接口定义语言,可以使当前的应用绑定一个其他进程的Service,调用远程服务实现跨进程通信。

一、基本使用

首先我们要说明的是AIDL可以实现进程间通讯,我们则需要有两个APP进程,分为服务端和客户端,服务端设置被调用的Service,而客户端则是去调用服务端的Service中的内容,在此以两个APP为例进行说明。

1. 服务端AIDL文件创建

我们首先建立一个APP工程作为服务端,右键新建一个AIDL文件:

在这里插入图片描述

创建一个文件:IMyAidlInterface.aidl,代码如下:

// IMyAidlInterface.aidl
package com.example.workdemo2;

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

interface IMyAidlInterface {

    String getName();
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

其中basicTypes()是默认方法,可以直接空实现,getName()是我们自定义的方法,创建完成之后,我们需要执行一下build。

支持的基本数据类型

默认情况下AIDL支持如下几种基本数据类型:

  • Java的基本数据类型:int,long,boolean,double等。
  • String字符串
  • 自定义的Parcelable对象
  • List:List中类型需要是上述的数据类型或者是Parcelable类型
  • Map:Map中类型需要是上述的数据类型或者是Parcelable类型
2. 服务端Service创建

由于服务端是需要提供Service给客户端调用,所以需要创建一个Service,我们新建立一个Service——MyService,在Service中创建一个继承自刚才AIDL Stub的内部类MyBinder,实现其接口方法,在onBind中返回内部类实例,代码如下:

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            setForegroundService();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }
    class MyBinder extends IMyAidlInterface.Stub{

        @Override
        public String getName() throws RemoteException {
            return "测试数据";
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    }

    /**
     * 将service设置为前台服务
     */
    private void setForegroundService() {
        String channelId = "测试Service";
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel notificationChannel = manager.getNotificationChannel(channelId);
            if (notificationChannel == null) {
                notificationChannel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
                notificationChannel.setVibrationPattern(new long[0]);
                notificationChannel.setSound(null, null);
                manager.createNotificationChannel(notificationChannel);
            }
        }
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId);
        builder.setSound(null);
        builder.setVibrate(new long[]{});
        startForeground(101, builder.build());
    }
}

AndroidManifest.xml文件声明时,我们把Service声明为其他进程可以调用:

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_AIDL_MESSAGE1"></action>
            </intent-filter>
        </service>
3. 客户端AIDL同步

执行上述步骤之后,服务端的代码就基本完成了,下面我们首先把刚才在服务端的AIDL文件copy到客户端中,然后执行build。

需要注意的是客户端和服务端的AIDL文件的包名需要完全一致。

4. 客户端调用

同步了客户端的AIDL之后,下一步是需要将当前的呃服务绑定到客户端组件上,以绑定到Activity中为例,代码如下,基本上绑定Service的逻辑:

    /**
     * 绑定AIDL Service
     */
    private void bindAIDL() {
        Intent intent = new Intent();
        intent.setAction("android.intent.action.RESPOND_AIDL_MESSAGE1");
        Intent finalIntent = achieveExplicitFromImplicitIntent(this, intent);
//        intent.setPackage("com.example.workdemo2");
        Logger.d("设置Intent");
        bindService(finalIntent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                iMyAidlInterface = null;
                Logger.d("连接断开");
            }
        }, BIND_AUTO_CREATE);
    }

    /**
     * 设置AIDL intent
     *
     * @param context
     * @param implicitIntent
     * @return
     */
    public Intent achieveExplicitFromImplicitIntent(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;
    }

在Activity中调用bindAIDL()方法,之后就可以调用getName()方法了。

二、自定义数据类型

在实际开发中,只使用AIDL的默认类型是不够的,有时候需要使用自定义的类型,则需要将自定义类型继承Parcelable接口。

1. 自定义数据类型

我们新建一个People类,实现Parcelable接口,代码如下:

import android.os.Parcel;
import android.os.Parcelable;

public class People implements Parcelable {

    int age;
    String Name;

    public People(int age, String name) {
        this.age = age;
        this.Name = name;
    }

    protected People(Parcel in) {
        age = in.readInt();
        Name = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(age);
        dest.writeString(Name);
    }

    public static final Creator<People> CREATOR = new Creator<People>() {
        @Override
        public People createFromParcel(Parcel in) {
            return new People(in);
        }

        @Override
        public People[] newArray(int size) {
            return new People[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

}
2. AIDL文件添加

在完成了上述定义后,我们就可以将People对象添加到AIDL文件中,添加parcelable People;就可以是AIDL支持People数据对象,如下:

// IMyAidlInterface.aidl
package com.example.workdemo2;
//添加自定义的parcelable对象
parcelable People;

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

interface IMyAidlInterface {

    String getName();
    People getPeople();
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

响应的Service中的MyBinder类也需要同步接口变化:

    class MyBinder extends IMyAidlInterface.Stub{

        @Override
        public String getName() throws RemoteException {
            return "测试数据";
        }

        @Override
        public People getPeople() throws RemoteException {
            return new People(10,"jim");
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    }
3. AIDL同步

将AIDL文件和自定义类型Java文件同步copy到客户端代码中,调用方法和上述的使用基本相同,就不再赘述了。

需要注意的是AIDL文件和自定义类型的文件都必须要和服务端的包名保持一致。

总结

AIDL的基本使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值