Android AIDL回顾

概述

AIDL全名 Android Interface Definition Language,是一种接口定义语言,也是Android系统的一种跨进程通信机制,其本质是系统为我们提供的一种快速实现Binder的工具,是Binder最简单的应用。

关于数据

AIDL文件中并不是所有的数据都可以使用,能够使用的数据类型包括如下几种:

* 除short外的基本数据类型(int, long, char, boolean, double等)
* String 和 CharSequence
* List:只支持ArrayList,并且里面的元素都能被AIDL支持;作为参数时要指明输入还是输出
* Map:只支持HashMap,里面的每个元素能被AIDL支持
* Parcelable:所有实现Parcelable接口的对象
* AIDL: 所有AIDL接口本身也能在AIDL文件中使用

aidl接口文件

package com.cstar.musicplayer;

interface IAlexaMusicService {
    /**
     * 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);
    int getAlexaStatus();
}

编译之后编译器自动生成了一个服务接口类名.Stub的抽象类,它继承自Binder,然后实现了我们定义的服务接口(注意我们的服务接口实现了IInterface接口)。重点是它是一个Binder!其实,Android的AIDL就是让编译器帮助我们生成一个实现了我们接口的Binder,以帮助我们简化开发,例如IAlexaMusicService.aidl生成的如下文件:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /home/android/AndroidStudioProjects/MusicPlayer/app/src/main/aidl/com/cstar/musicplayer/IAlexaMusicService.aidl
 */
package com.cstar.musicplayer;
// Declare any non-default types here with import statements

public interface IAlexaMusicService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.cstar.musicplayer.IAlexaMusicService
{
private static final java.lang.String DESCRIPTOR = "com.cstar.musicplayer.IAlexaMusicService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.cstar.musicplayer.IAlexaMusicService interface,
 * generating a proxy if needed.
 */
public static com.cstar.musicplayer.IAlexaMusicService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.cstar.musicplayer.IAlexaMusicService))) {
return ((com.cstar.musicplayer.IAlexaMusicService)iin);
}
return new com.cstar.musicplayer.IAlexaMusicService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getAlexaStatus:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getAlexaStatus();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);//data是传入数据,reply是返回数据,两个数据都是Parcel类说明在AIDL的通信过程中数据必须经过序列化操作。
}
private static class Proxy implements com.cstar.musicplayer.IAlexaMusicService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int getAlexaStatus() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getAlexaStatus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getAlexaStatus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int getAlexaStatus() throws android.os.RemoteException;
}

根据aidl文件生成的接口文件继承android.os.IInterface接口, 定义了我们在AIDL文件中定义的方法,然后还有个内部静态类Stub,对于这个Stub:

int常量: Stub中其中两个int常量是用来标识我们在接口中定义的方法

DESCRIPTOR常量: 是Binder的唯一标识。通常,DESCRIPTOR描述会直接使用包名 + 服务接口

asInterface 方法: 用于将服务端的Binder对象转换为客户端所需要的接口对象,该过程区分进程,如果进程一样,就返回服务端Stub对象本身,否则呢就返回封装后的Stub.Proxy对象。

onTransact 方法: 运行在服务端的Binder线程中的,当客户端发起远程请求后,在底层封装后会交由此方法来处理。通过code来区分客户端请求的方法.。如果该方法返回false的换,客户端的请求就会失败。一般可以用来做权限控制。data是传入数据,reply是返回数据,两个数据都是Parcel类说明在AIDL的通信过程中数据必须经过序列化操作。

Proxy代理类:
比如其中getAlexaStatus()方法运行在客户端,当客户端发起远程请求时,_data会写入参数然后调用transact方法发起RPC(远程过程调用)请求,同时挂起当前线程,然后服务端的 onTransact方法就会被 调起,直到RPC过程返回后,当前线程继续执行,并从_reply取出返回值(如果有的话),并返回结果。


AIDL调用服务端方法后,会挂起等待,如果服务端进行执行大量耗时操作,会导致客户端ANR。解决方法:客户端调用放在非UI线程即可。

自定义引用类型

使用自定义的数据类型,需要先为它也生成一个aidl文件如下:
ResultInfo.aidl

package android.app; //AIDL的包名

/** @hide */
parcelable ResultInfo;//类名

创建对应的java数据类实现Parcelable接口,注意包名需要和aidl一样

package android.app;

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

/**
 * {@hide}
 */
public class ResultInfo implements Parcelable {
    public final String mResultWho;
    public final int mRequestCode;
    public final int mResultCode;
    public final Intent mData;

    public ResultInfo(String resultWho, int requestCode, int resultCode,
            Intent data) {
        mResultWho = resultWho;
        mRequestCode = requestCode;
        mResultCode = resultCode;
        mData = data;
    }

    public String toString() {
        return "ResultInfo{who=" + mResultWho + ", request=" + mRequestCode
            + ", result=" + mResultCode + ", data=" + mData + "}";
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeString(mResultWho);
        out.writeInt(mRequestCode);
        out.writeInt(mResultCode);
        if (mData != null) {
            out.writeInt(1);
            mData.writeToParcel(out, 0);
        } else {
            out.writeInt(0);
        }
    }

    public static final Parcelable.Creator<ResultInfo> CREATOR
            = new Parcelable.Creator<ResultInfo>() {
        public ResultInfo createFromParcel(Parcel in) {
            return new ResultInfo(in);
        }

        public ResultInfo[] newArray(int size) {
            return new ResultInfo[size];
        }
    };

    public ResultInfo(Parcel in) {
        mResultWho = in.readString();
        mRequestCode = in.readInt();
        mResultCode = in.readInt();
        if (in.readInt() != 0) {
            mData = Intent.CREATOR.createFromParcel(in);
        } else {
            mData = null;
        }
    }
}

自定义类型使用了in或者inout标识符的话,需要给自定义类实现readFromParcel()方法。比如:

public void readFromParcel(Parcel in) {
      mResultWho = in.readString();
}

然后在定义aidl接口时import

参数修饰符
in: 表示参数数据只能由客户端传递到服务端,基本类型就默认只支持in修饰符。
out:表示参数数据只能由服务端传递到客户端。即服务端如果修改了参数对象的值,那么客户端的值也会变化,但是服务端无法读取到客户端对象的值。
inout:表示参数数据能够双向传递。

```
package com.cstar.musicplayer;
import com.cstar.musicplayer.ResultInfo;
interface IAlexaMusicService {
    /**
     * 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);
    int getAlexaStatus();
    //参数修饰符是out,表示参数数据只能由服务端传递到客户端,如果是in的话,那么ResultInfo要实现readFromParcel
    void getResult(out ResultInfo);   
}

关于AIDL的包结构

AIDL的包结构在服务端和客户端要保持一致,否则出错,因为客户端要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样的话,就无法成功反序列化。

关于使用

1、定义AIDL文件(先在服务端定义,客户端也要定义,内容路服务端一样)

2、服务端实现接口

3、客户端调用接口

三、AIDL语法规则

语法规则如下:

1、语法跟Java的接口很相似

2、AIDL只支持方法,不能定义静态成员

3、AIDL运行方法有任何类型的参数(除short外)和返回值

4、除默认类型,均需要导包

猜你喜欢

转载自blog.csdn.net/ch853199769/article/details/80246008