【Android基础】应用层AIDL接口的实现

【Android基础】应用层AIDL接口的实现

本文介绍了应用层定义AIDL接口并使用其通信的简单手法。

通常包括以下步骤

定义AIDL接口文件

首先,通信的服务端和客户端,需要创建一个AIDL文件来定义接口。AIDL文件类似于Java接口文件,但它使用AIDL语法。例如,你可以创建一个名为IAirConditionerService.aidl的文件,并在其中定义你的接口方法。客户端和服务端,需要在同一个包名的文件夹里面创建。

interface IAIRConditionerService {
    void setTemperature(int temperature);
    int getTemperature();
    // 其他方法...
}

编译AIDL文件

在Android Studio中,点击build之后,AIDL文件会自动编译。编译后,会生成一个Java接口文件,位于app/build/generated/source/aidl/debug/目录下(假设你的项目是在debug模式下编译的)。这个生成的Java接口文件包含了与AIDL文件中定义的接口相对应的方法。

实现服务端的Service类

接下来,你需要创建一个Service类来实现AIDL接口。这个Service类将处理来自客户端的请求。在Service类中,你需要实现AIDL接口中定义的所有方法。

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class AirConditionerService extends Service {
    private final IAIRConditionerService.Stub binder = new IAIRConditionerService.Stub() {
        @Override
        public void setTemperature(int temperature) throws RemoteException {
            // 实现设置温度的逻辑
        }

        @Override
        public int getTemperature() throws RemoteException {
            // 实现获取温度的逻辑
            return 0;
        }
        // 其他方法的实现...
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}

然后在AndroidManifest.xml中注册Service:你需要在AndroidManifest.xml文件中注册你的Service,以便系统能够找到并启动它。

客户端调用

在客户端应用中,你可以通过绑定到Service来调用AIDL接口中的方法。首先,你需要创建一个ServiceConnection对象,并在其中实现onServiceConnected和onServiceDisconnected方法。然后,你可以使用bindService方法来绑定到Service。

// 客户端Activity或Service
public class AirConditionerClientActivity extends AppCompatActivity {
    private IAIRConditionerService airConditionerService;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            airConditionerService = IAIRConditionerService.Stub.asInterface(service);
            try {
                int temperature = airConditionerService.getTemperature();
                // 处理返回的温度值
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

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

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

        Intent intent = new Intent(this, AirConditionerService.class);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

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

一般手机平台上,两个三方app之间要建立这个连接,客户端需要在AndroidManifest里面加一条权限声明:

  <uses-permission
        android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />

实际的例子

这是我的第一个Android项目,模仿网易云音乐开发了一个约等于静态界面的app,体验了AIDL接口的跨进程通信。

  • 客户端:“网抑云”app
  • 服务端:一个扫描sdcard内音乐文件的服务进程

直接先看项目文件:

Server

server

首先就是aidl文件夹,里面定义了通信的接口声明aidl和需要传输的实体类Music的aidl.

AIDLtest.aidl

package com.example.server;
import com.example.server.Music;

interface AIDLtest {

    int add(int num1, int num2);
    List<Music> getmusiclist();
    List<Music> addmusic();
}

我们扫描到音频文件之后,需要将信息通过Music类包装传过去。

Music.aidl

// Music.aidl
package com.example.server;

parcelable Music;

Mudic.java

package com.example.server;

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

public class Music implements Parcelable {
    public String name;
    public String singer;

    public Music() {
    }

    protected Music(Parcel in) {
        name = in.readString();
        singer = in.readString();
    }

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSinger() {
        return singer;
    }

    public void setSinger(String singer) {
        this.singer = singer;
    }

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

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

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

java类需要实现Parcelable接口才可以顺利传输,写法是比较固定的。

Client

client

客户端建立aidl文件夹时,各个类所属的包名一定要和服务端一致,可以看到图片中aidl文件夹是直接从服务端移植过来的。

需要注意的是,实体类Music,java文件的定义,也需要和服务端的包名一致,这里是单独建了一个包来放置它。

至于后面的调用流程就和上面一节提到的是一致的了。

车载Android上普遍的方式

上面的是手机端比较基础的通信集成方式,在车载Android领域,需要跨进程通信时,往往会采取对客户端最友好的方式来实现。

我们会在服务端的代码仓库里面直接新建一个libary的模组。

或者单独做一个仓库,来放置不同app的对外接口文件。

在这些地方,由服务端,也就是接口提供方的开发工程师,来完成客户端的Service连接逻辑,并把方法包装成aar提供给需求方。

然后客户端需求方的项目去集成这个aar,自己决定什么时机调用。