AIDL简介
AIDL(Android Interface Definition Language) 是Android IPC(Inter-Process Communication)进程间通信的一种重要方式,使用Binder实现。我们知道在Android(Linux)系统中,每个进程都有独立的内存区域,其他进程无法直接访问,当我们的进程中需要跨进程调用方法、获取数据时,我们就可以使用AIDL定义客户端与服务均认可的编程接口,实现进程间的相互通信。
AIDL使用
下面我们先通过一个简单的例子来熟悉一下使用aidl实现进程间通信的流程。我们先main目录下创建aidl目录,并们创建一个.aidl文件,定义一些方法。
// ImageInterface.aidl
package com.chuan.infrastructure;
// Declare any non-default types here with import statements
interface ImageInterface {
void addImage(String path);
String getImage(int id);
/**
* 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);
}
然后重新rebuild一下工程,会发现AndroidStudio 会帮我们在build目录下生成与aidl同名的Java文件。
至此我们跨进程的接口就已经定义好了,需要跨进程的服务继承接口的静态内部类Stub即可。我们把创建好的Service配置在其他进程里面与主进程的activity去交互。
ImageService
package com.chuan.infrastructure.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.chuan.infrastructure.ImageInterface;
import java.util.ArrayList;
import java.util.List;
public class ImageService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ImageBinder();
}
//继承自动生成的静态内部类,使当前类可以跨进程
public static class ImageBinder extends ImageInterface.Stub{
List<String> images = new ArrayList<>();
@Override
public void addImage(String path) {
images.add(path);
}
@Override
public String getImage(int id) {
if (id>=0 && id < images.size()){
return images.get(id);
}
return null;
}
}
}
在AndroidManifest.xml里面配置ImageService在独立进程里面。
<service android:name=".service.ImageService"
android:process=":images"
android:exported="true"
android:enabled="true"/>
然后再在activity里面绑定ImageService,此时activity已经和ImageService在不同进程。ImageService进程名为 -> 包名:images
ImageActivity
package com.chuan.infrastructure;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import com.chuan.infrastructure.service.ImageService;
public class ImageActivity extends AppCompatActivity {
private static final String TAG = ImageActivity.class.getSimpleName();
private ServiceConnection connection;
private ImageInterface imageInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
imageInterface = ImageInterface.Stub.asInterface(service);
Log.d(TAG, "onServiceConnected: " + imageInterface.toString());
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent = new Intent(getApplication(), ImageService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
public void addImage(View view){
try {
imageInterface.addImage("https://www.image.com/image/a.jpg");
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void getImage(View view){
try {
//获取远程保存的数据
String url = imageInterface.getImage(0);
Log.d(TAG, "getImage: " + url);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}
在绑定service成功后,service返回给我们了Sub的代理类对象,而且我们通过该对象成功滴与ImageService通信。
至此,我们的跨进程通信已经完成,已经可以实现简单的跨进程业务。但是大多数人还对AIDL没有一个清晰的认知,只是一脸懵逼的实现了跨进程通信。
AIDL解析
我们前面说过AIDL是接口定义语言,他的作用仅仅就是定义一个规范,然后由AndroidStudio帮我们生成具体跨进程规则的Java接口和类,基于Binder实现跨进程通信,那么我们就可以参考自动生成的代码自己写一套跨进程通信的接口,从而不用像最开始一样创建一个.aidl文件,再rebuild工程生成一个同名的Java文件。
下面我们对生成的Java接口文件做一个详解,具体的解析我都加在注释里面
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.chuan.infrastructure;
//ImageInterface 继承自 IInterface,
// IInterface其实就是AIDL提供的接口用了定义借口包含哪些能力
public interface ImageInterface extends android.os.IInterface
{
//静态内部类继承Binder,实现了上面定义的接口ImageInterface
// 说明该静态内部类具备接口定义的功能,同时继承的Binder类实现了IBinder接口,
// IBinder接口定义了跨进程的能力,所以Stub子类的对象可以跨进程,而且具备ImageInterface接口定义的所有功能
public static abstract class Stub extends android.os.Binder implements ImageInterface
{
private static final String DESCRIPTOR = "com.chuan.infrastructure.ImageInterface";
public Stub()
{
//设置interface对象和描述,在后面queryLocalInterface获取
this.attachInterface(this, DESCRIPTOR);
}
//该方法用来用来判断是否在同一进程,
// 同一进程返回ImageInterface对象,不在同一进程返回Stub的代理类对象
public static ImageInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//具体判断逻辑,在stub的构造方法中调用了this.attachInterface(this, DESCRIPTOR);
//设置在了Server进程,Client进程查询拿到null,所以返回代理对象Proxy
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof ImageInterface))) {
return ((ImageInterface)iin);
}
return new Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
//跨进程调用具体执行方法,跨进程通过proxy类调用
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_addImage:
{
data.enforceInterface(descriptor);
String _arg0;
_arg0 = data.readString();
//调用stub具体实现类的方法
this.addImage(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getImage:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
String _result = this.getImage(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements ImageInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public void addImage(String path) 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.writeString(path);
//跨进程调用,调用所代理类的onTransact,
boolean _status = mRemote.transact(Stub.TRANSACTION_addImage, _data, _reply, 0);
//该判断用以支持同进程调用
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addImage(path);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public String getImage(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
boolean _status = mRemote.transact(Stub.TRANSACTION_getImage, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getImage(id);
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static ImageInterface sDefaultImpl;
}
static final int TRANSACTION_addImage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getImage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(ImageInterface impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static ImageInterface getDefaultImpl() {
return Proxy.sDefaultImpl;
}
}
void addImage(String path) throws android.os.RemoteException;
String getImage(int id) throws android.os.RemoteException;
}
到此我们的AIDL已基本熟悉流程,我们可以不用创建.aidl文件,而是自己继承android.os.IInterface、android.os.Binder 去实现跨进程通信了。下面是我写的一个简单的跨进程计算求和的接口,去掉了支持同进程求和,代码精简了不少,不到80代码,没有钱捕获异常,简单测试使用,供大家参考。
package com.chuan.infrastructure;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public interface CalcInterface extends IInterface {
abstract class CalcStub extends Binder implements CalcInterface{
private static final int TRANSACTION_SUM = 1;
private static final String DESCRIPTOR = CalcInterface.class.getName();
public CalcStub(){
this.attachInterface(this, DESCRIPTOR);
}
@Override
public IBinder asBinder() {
return this;
}
public static CalcInterface asInterface(IBinder binder){
if (binder == null) return null;
IInterface owner = binder.queryLocalInterface(DESCRIPTOR);
if (owner != null && owner instanceof CalcInterface){
return (CalcInterface) owner;
}else {
return new Proxy(binder);
}
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code){
case TRANSACTION_SUM:{
data.enforceInterface(DESCRIPTOR);
int a = data.readInt();
int b = data.readInt();
int sum = this.sum(a, b);
reply.writeNoException();
reply.writeInt(sum);
return true;
} default:{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy extends CalcStub{
private IBinder remote;
Proxy(IBinder stub){
remote = stub;
}
@Override
public int sum(int a, int b) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
data.writeInterfaceToken(DESCRIPTOR);
data.writeInt(a);
data.writeInt(b);
remote.transact(TRANSACTION_SUM, data, replay, 0);
replay.readException();
int result = replay.readInt();
data.recycle();
replay.recycle();
return result;
}
@Override
public IBinder asBinder() {
return remote;
}
}
}
int sum(int a, int b) throws android.os.RemoteException;
}
一下是测试结果截图: