进程间通信-AIDL

234 阅读3分钟

一、AIDL

安卓接口定义语言,用于进程间进行ipc通信。 image.png 详细:Android 接口定义语言 (AIDL)  |  Android 开发者  |  Android Developers

  • AIDL接口是直接函数调用。
  • 本地进程的调用在发起调用的同一线程执行。
  • AIDL接口必须线程安全。

安卓的序列化

❤️Android 序列化(Serializable和Parcelable) ❤️ - 掘金 (juejin.cn)

Parcelable和Bundle的区别

Parcelable 和 Bundle  |  Android 开发者  |  Android Developers

android binder机制中Parcel容器到底做了什么? - 知乎 (zhihu.com)

二、如何使用AIDL?

1、服务端

编写parcelable对象

public class NumberData implements Parcelable {
    public int num1;
    public int num2;
    protected NumberData(Parcel in) {
        readFromParcel(in);
    }

    protected NumberData() {

    }

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

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

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

    @Override
    public void writeToParcel(@NonNull Parcel out, int i) {
        out.writeInt(num1);
        out.writeInt(num2);
    }

    public void readFromParcel(Parcel in) {
        num1 = in.readInt();
        num2 = in.readInt();
    }
}

编写 .aidl文件

interface MyAidlInterface {
 
    // 直接传入两个值进行相加
    int add(int a,int b);

    // 传入一个对象:a和b,相加后返回
    int add1(in NumberData data);

}

实现接口

// 实现.stub()
private final MyAidlInterface.Stub mBinder = new MyAidlInterface.Stub(){

    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
    //通过ipc接受对象
    @Override
    public int add1(NumberData data) throws RemoteException {
        return data.num1 + data.num2;
    }

};
  • 如果接口的方法调用时间很长,应提前做好在客户端调用时新开线程处理的准备
  • 保证线程安全
  • 抛出的异常不会传递给调用方(客户端)

暴露接口,供客户端使用

public class MyService extends Service {

    public MyService(){

    }
    // 通过onBind暴露接口,提供客户端调用
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    // 实现.stub()
    private final MyAidlInterface.Stub mBinder = new MyAidlInterface.Stub(){

        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }
        //通过ipc接受对象
        @Override
        public int add1(NumberData data) throws RemoteException {
            return data.num1 + data.num2;
        }
    };
}

同时也需要暴露parcelable对象

package com.zx.binderserver;

parcelable NumberData;

开启服务,并配置权限

项目结构image.png

2、客户端

创建同样目录结构的.adil接口

从server复制相同的代码到client

image.png

创建service,并实现接口

public class MyService extends Service {

    private final Binder mbinder = new MyBinder();
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d("MainActivity", "onBind()");
        return mbinder;
    }
    
    private class MyBinder extends MyAidlInterface.Stub {

        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }

        @Override
        public int add1(NumberData data) throws RemoteException {
            return data.num1 + data.num2;
        }
    }
}

创建serviceConnection进行调用

// MainActivity.java中
// 需要编译后,才可以识别到进行调用
private MyAidlInterface mService;

private boolean isConn = false;

private final ServiceConnection conn = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.d(TAG, "onServiceConnected()");
        mService = MyAidlInterface.Stub.asInterface(iBinder);
        isConn = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        unbindService(conn);
        isConn = false;
    }
};

调用业务逻辑

public class MainActivity extends AppCompatActivity {

    private boolean isConn = false;

    private final String TAG = this.getClass().getSimpleName();
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreate()");
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_main);
        // 未绑定服务,进行绑定
        if (!isConn) {
            mBindService();
            isConn = true;
        }

        Button btn = this.findViewById(R.id.btn);
        btn.setOnClickListener(view -> {
            try {
                TextView et1 = this.findViewById(R.id.et_1);
                TextView et2 = this.findViewById(R.id.et_2);
                Log.d(TAG, "et1=" + et1.getText() + ",et2=" + et2.getText());
                // 判空
                if (!TextUtils.isEmpty(et1.getText()) && !TextUtils.isEmpty(et1.getText())) {
                    int num1 = Integer.parseInt(et1.getText().toString());
                    int num2 = Integer.parseInt(et2.getText().toString());
                    Log.d(TAG, "a = " + num1 + ",b = " + num2);
                    // 远程进行计算结果
                    int res = mService.add(num1, num2);
                    Log.d(TAG, "res = " + res);
                    TextView tv = findViewById(R.id.tv);
                    // 展示结果
                    tv.setText(Integer.toString(res));
                }

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        });

        Button btn1 = this.findViewById(R.id.btn1);
        btn1.setOnClickListener(view -> {
            try {
                TextView et1 = this.findViewById(R.id.et_1);
                TextView et2 = this.findViewById(R.id.et_2);
                Log.d(TAG, "et1=" + et1.getText() + ",et2=" + et2.getText());
                // 判空
                if (!TextUtils.isEmpty(et1.getText()) && !TextUtils.isEmpty(et1.getText())) {
                    int num1 = Integer.parseInt(et1.getText().toString());
                    int num2 = Integer.parseInt(et2.getText().toString());
                    Log.d(TAG, "a = " + num1 + ",b = " + num2);
                    // 创建parcelable对象,存储需要相加的两个值
                    NumberData numberData = new NumberData();
                    numberData.setNum1(num1);
                    numberData.setNum2(num2);
                    // 调用远程的接口方法
                    int res = mService.add1(numberData);
                    Log.d(TAG, "res = " + res);
                    TextView tv = findViewById(R.id.tv);
                    // 展示结果
                    tv.setText(Integer.toString(res));
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        });
    }

    private void mBindService(){
        Log.d(TAG, "绑定服务....");
        // 创建Intent,并添加action
        Intent intent = new Intent("AIDL.server");
        // 设置包名
        intent.setPackage("com.zx.binderserver");
        // 绑定服务
        boolean isBind = bindService(intent, conn, Context.BIND_AUTO_CREATE);
        Log.d(TAG, "isBind = " + isBind);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 销毁服务
        if (isConn) {
            unbindService(conn);
            isConn = false;
        }
    }
}

三、遇到的各种问题

1、server和client两端的.aidl要处于同一项目结构

2、无法调用.aidl接口的方法时,需要先进行编译后,则可进行调用