AIDL简单使用

本文介绍了Android的AIDL(Android Interface Definition Language)用于进程间通信(IPC)的基本用法。AIDL的主要应用场景是IPC,当需要服务端并发处理多个请求时使用。AIDL使用步骤包括编写.AIDL文件、实现接口、暴露给客户端。文章通过一个实例展示了AIDL的使用,包括服务端和客户端的代码实现,并提供了进程间通信的流程图。此外,还提到了 Stub 和 Proxy 类在IPC中的作用,它们分别作为服务端和客户端的中间者。最后,推荐了进一步学习AIDL和Binder机制的文章。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AIDL是Android Interface Definition Language, 顾名思义,它主要就是用来定义接口的一种语言。Android提供AIDL主要用来进程间通讯。

AIDL的功能来看,它主要的应用场景就是IPC。虽然同一个进程中的client-service也能够通过AIDL定义接口来进行通信,但这并没有发挥AIDL的主要功能。 概括来说:

  1. 如果不需要IPC,那就直接实现通过继承Binder类来实现客户端和服务端之间的通信。
  2. 如果确实需要IPC,但是无需处理多线程,那么就应该通过Messenger来实现。Messenger保证了消息是串行处理的,其内部其实也是通过AIDL来实现。
  3. 在有IPC需求,同时服务端需要并发处理多个请求的时候,使用AIDL才是必要的

AIDL的简单使用步骤如下:

  1. 编写.AIDL文件,定义需要的接口
  2. 实现定义的接口
  3. 将接口暴露给客户端调用

下面在AS上创建一个工程来使用一下:

创建Aidl文件:

创建完后,可以看到aidl接口文件里面已经为我们提供了案例:

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

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

interface IMyAidlInterface {
    /**
     * 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);
}
这里先说下AIDL支持下列所述的数据类型:

  • 所有的基本类型(int、float等)
  • String
  • CharSequence
  • List
  • Map

如果要使用自定义的类型,必须实现Parcelable接口才能进行进程间通讯。

下面自定义HelloMsg类:

public class HelloMsg implements Parcelable {
    private String name;
    private int age;

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

    protected HelloMsg(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

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

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

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

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

    @Override
    public String toString() {
        return "HelloMsg{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

定义好HelloMsg.java之后,还需要新增一个与其对应的AIDL文件。那么同样按照刚才的步骤右键src文件夹,添加一个名为IHelloMsgInterface的AIDL文件。

// IHelloMsgInterface.aidl
package com.example.aidltest;
parcelable HelloMsg;

注意到parcelable的首字母是小写的,这算是AIDL一个特殊的地方。
接下来还需要修改IMyAidlInterface.aidl文件,如下:

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

import com.example.aidltest.HelloMsg;

interface IMyAidlInterface {
    HelloMsg sayHello();
}


 即便IMyAidlInterface.aidl和IHelloMsgInterface.aidl位于同一个包下,这里的import是必须要有的。这也是AIDL一个特殊的地方。


 注意:build之后发现会报错,将IHelloMsgInterfece.aidl重命名为HelloMsg.aidl即可。


build成功之后会在build/generated/souce/aidl/debug目录下生成IMyAidlInterface文件。可以大致熟悉下该文件的内容,对掌握android binder机制很有帮助

public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.example.aidltest.IMyAidlInterface interface,
 * generating a proxy if needed.
 */
public static com.example.aidltest.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidltest.IMyAidlInterface))) {
return ((com.example.aidltest.IMyAidlInterface)iin);
}
return new com.example.aidltest.IMyAidlInterface.Stub.Proxy(obj);
}


这里有个内部类Stub,它继承系统Binder类和实现IMyAidlInterface接口。另外还提供了asInterface()接口,这个方法接受一个远端Binder对象,并将其转化成Stub对应的接口对象并返回。在构造方法调用Binder中的attachInterface方法把当前服务对象和描述符进行关联。在asInterface方法中会调用queryLocalInterface查询,如果不在同一进程就返回null,这个时候就返回Proxy对象。

上面看完了Stub类之后,发现他其实是远端服务Binder对象的一个中间者,用来和客户端进行交互的,下面再来看一下Proxy类:

private static class Proxy implements com.example.aidltest.IMyAidlInterface
{
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 com.example.aidltest.HelloMsg sayHello() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.aidltest.HelloMsg _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.aidltest.HelloMsg.CREATOR.createFromParcel(_reply);
}

可以看到里面有个mRemote对象,它是服务端传递过来的binder对象。调用transact方法后会调用上面Stub中的onTransact方法。这里其实用了静态代理模式,Proxy就是远端传递过来的binder的本地代理。可以理解为客户端的中间者。

Stub类是服务端的中间者,一般是实现了AIDL接口类型和继承了Binder类,具备将Binder对象转化成原生对象的能力
Proxy类是客户端的中间者,一般是实现了AIDL接口类型

下面实现服务端的接口,定义RemoteService.java

public class RemoteService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IMyAidlInterface.Stub() {
            @Override
            public HelloMsg sayHello() throws RemoteException {
                return new HelloMsg("wuliqing", 28);
            }
        };
    }
}

客户端调用服务端接口代码如下:

public class MainActivity extends AppCompatActivity {
    private IMyAidlInterface iMyAidlInterface = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, RemoteService.class);
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(serviceConnection);
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

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

    public void onClickToSayHello(View view) {
        if (iMyAidlInterface != null) {
            try {
                HelloMsg helloMsg = iMyAidlInterface.sayHello();
                Toast.makeText(this, helloMsg.toString(), Toast.LENGTH_SHORT).show();

            } catch (RemoteException e) {
                e.printStackTrace();
                Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
            }
        }
    }
}

onServiceConnected()回调中,我们使用IMyAidlInterface.Stub.asInterface(service)方法返回我们的接口的引用。接着客户端就可以通过它来对服务端发送请求了。

在这里我为RemoteService设置了process属性,让它运行在与默认进程不同的进程中。

        <service
            android:name="RemoteService"
            android:process=":remote" />

从上图可看出客户端和服务运行在两个进程当中。

然后点击按钮,成功返回结果。

最后给出一张流程图,加深印象:


关于AIDL和binder机制可参考下面文章:

Android中AIDL的基本用法

Binder机制和远程服务调用机制分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值