五、AIDL(二)不同应用中使用、自定义数据类型及定向Tag

一、Service 开启和停止
二、Service 执行耗时工作
三、IPC常用方式
四、AIDL(一)同一应用中使用AIDL及原理
五、AIDL(二)不同应用中使用、自定义数据类型及定向Tag
六、AIDL(三)实现回调
七、AIDL(四)获取服务及绑定和Binder传递流程


AIDL(二)不同应用中使用、自定义数据类型及定向Tag

5.1 项目介绍

5.1.1 整体介绍

本项目中,因为探究的是不同应用中使用AIDL、在AIDL中自定义数据类型以及定向Tag的使用,所以项目中有两个app(分别为:AndroidIPCServer:服务端、AndroidIPCClient:客户端)、一个自定义数据类(VectorQuantity),两个AIDL文件(IVectorQuantityInterfaceVectorQuantity)。

我们知道,AIDL中在服务端和客户端之间会传递数据,但是传递的数据在双端却不是同一个对象,为确保两端对象修改的一致性,定义了定向Tag,定向Tag有三种,分别表示了在跨进程通信中数据的流向:

  • in:其中 in 表示数据只能由客户端流向服务端,表现为服务端将会接收到传递对象的完整数据,但是服务端对数据的修改不会传递到客户端;
  • out表示数据只能由服务端流向客户端,表现为服务端接收到的数据对象是空对象(由无参构造器创建),但是在服务端对数据的修改会传递到客户端;
  • inout表示数据可在服务端与客户端之间双向传递,表现为客户端传递到服务端的数据服务端可以接收到,服务端对数据的修改也可以传递到客户端;

5.1.2 定向Tag实验

为验证inoutinout三个定向Tag功能,我们可以定义一个自定义数据类VectorQuantity,该类是一个一维向量,详细的内容参考后面的类代码。结合VectorQuantity类和定向Tag,设计出下面实验:

  1. in tag:在客户端数据为[1,2],随后服务端将数据修改为[11,12]。根据理论结果,服务端接收到的数据为[1,2],但是修改的数据[11,12]不能传递到客户端,所以,客户端数据仍为[1,2]
  2. our tag:在客户端数据为[3,4],随后将数据修改为[13,14]。根据理论结果,[3,4]不能传递到服务端,服务端的数据为[],但是修改的数据[13,14]可以传递到客户端,所以,客户端数据改变为[13,14]
  3. inout tag:在客户端数据为[5,6],随后将数据修改为[15,16]。根据理论结果,[5,6]可以传递到服务端,服务端的数据为[5,6],修改的数据[13,14]也可以传递到客户端,所以,客户端数据改变为[15,16]

5.2 项目代码

5.2.1 自定义数据类型

代码1

com.ieening.androidipcserver.vectorquantity.VectorQuantity

package com.ieening.androidipcserver.vectorquantity;

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

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VectorQuantity implements Parcelable { // 注释1
    private static final String REGEX_PATTERN = "(?<=\\()( *?\\d+? *?,?)+?(?=\\))";

    public ArrayList<Double> getComponents() {
        return components;
    }

    public int getDimension() {
        return components.size();
    }

    private final ArrayList<Double> components;

    protected VectorQuantity(Parcel in) {
        this();
        for (double component :
                Objects.requireNonNull(in.createDoubleArray())) {
            this.components.add(component);
        }
    }

    public VectorQuantity() { // 注释2
        this.components = new ArrayList<>();
    }

    public static final Creator<VectorQuantity> CREATOR = new Creator<VectorQuantity>() { // 注释3
        @Override
        public VectorQuantity createFromParcel(Parcel in) {
            return new VectorQuantity(in);
        }

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

    public VectorQuantity(ArrayList<Double> arrayList) {
        this();
        this.components.addAll(arrayList);
    }

    public static VectorQuantity getNewInstance(String componentsString) {
        ArrayList<Double> doubles = getDoublesFromString(componentsString);
        return new VectorQuantity(doubles);
    }

    @NonNull
    private static ArrayList<Double> getDoublesFromString(String componentsString) {
        String componentsStringTrim = componentsString.trim();
        Matcher matcher = Pattern.compile(REGEX_PATTERN).matcher(componentsStringTrim);
        ArrayList<Double> doubles = new ArrayList<>();
        while (matcher.find()) {
            int begin = matcher.start();
            int end = matcher.end();
            for (String component : componentsStringTrim.substring(begin, end).split(",")) {
                doubles.add(Double.parseDouble(component.trim()));
            }
        }
        return doubles;
    }

    @NonNull
    @Override
    public String toString() {
        return this.components.toString();
    }

    @Override
    public int describeContents() { // 注释4
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) { // 注释5
        dest.writeDoubleArray(Arrays.stream(this.components.toArray()).mapToDouble(k -> (double) k).toArray());
    }

    public void update(String componentsString) {
        ArrayList<Double> doubles = getDoublesFromString(componentsString);
        this.components.clear();
        this.components.addAll(doubles);
    }

    public void update(VectorQuantity vectorQuantity) {
        this.components.clear();
        this.components.addAll(vectorQuantity.getComponents());
    }

    public void readFromParcel(Parcel in) { // 注释6
        this.components.clear();
        for (double component :
                Objects.requireNonNull(in.createDoubleArray())) {
            this.components.add(component);
        }
    }
}

进行AIDL通信时,需要对数据进行序列化,我们通过实现Parcelable 接口来实现序列化。

  1. 注释1:实现Parcelable接口;
  2. 注释3、注释4、注释5:实现Parcelable接口中的方法;
  3. 注释6:在AIDL中,如果需要支持outinout定向标签,还要求添加readFromParcel方法;
  4. 注释2:需要有无参构造器,否则编译时会出错;

5.2.2 服务端代码

服务端代码结构如下:

aidl different application server file structure

5.3.2.1 aidl 文件

下面VectorQuantity.aidl文件,定义VectorQuantity parcelable对象,以供其他AIDL文件使用。

代码2

com/ieening/androidipcserver/vectorquantity/VectorQuantity.aidl

// VectorQuantity.aidl
package com.ieening.androidipcserver.vectorquantity;

// Declare any non-default types here with import statements
parcelable VectorQuantity;

代码3

com/ieening/androidipcserver/vectorquantity/IVectorQuantityInterface.aidl

// IVectorQuantityInterface.aidl
package com.ieening.androidipcserver.vectorquantity;

// Declare any non-default types here with import statements
import com.ieening.androidipcserver.vectorquantity.VectorQuantity; // 注释1

interface IVectorQuantityInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void updateVectorQuantityIn(in VectorQuantity vectorQuantity); // 注释2
    void updateVectorQuantityOut(out VectorQuantity vectorQuantity); // 注释3
    void updateVectorQuantityInOut(inout VectorQuantity vectorQuantity); // 注释4
}
  1. 注释1:导入VectorQuantity parcelable对象;
  2. 注释2:定义updateVectorQuantityIn方法,注意参数使用in修饰;
  3. 注释3:定义updateVectorQuantityOut方法,注意参数使用out修饰;
  4. 注释4:定义updateVectorQuantityInOut方法,注意参数使用inout修饰;
5.2.2.2 Service 代码

代码4

com.ieening.androidipcserver.vectorquantity.VectorQuantityService

package com.ieening.androidipcserver.vectorquantity;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import com.ieening.androidipcserver.MainActivity;

public class VectorQuantityService extends Service {
    private final static String TAG = VectorQuantityService.class.getName();

    public static final String UPDATE_EDIT_TEXT_ACTION = "action_update_receive_from_client_edit_text";

    private final IVectorQuantityInterface.Stub iVectorQuantityBinder = new IVectorQuantityInterface.Stub() { // 注释1

        @Override
        public void updateVectorQuantityIn(VectorQuantity vectorQuantity) {
            sendBroadcastToActivity(vectorQuantity, 1);

            Log.d(TAG, "receive from client updateVectorQuantityIn firstVectorQuantity = " + vectorQuantity);
            VectorQuantity newInstance = VectorQuantity.getNewInstance(MainActivity.firstVectorQuantityString);


            if (newInstance.getDimension() > 0) {
                vectorQuantity.update(newInstance);
                Log.d(TAG, "send to client updateVectorQuantityIn firstVectorQuantity = " + vectorQuantity);
            }
        }

        @Override
        public void updateVectorQuantityOut(VectorQuantity vectorQuantity) {
            sendBroadcastToActivity(vectorQuantity, 2);

            Log.d(TAG, "receive from client updateVectorQuantityOut secondVectorQuantity = " + vectorQuantity);
            VectorQuantity newInstance = VectorQuantity.getNewInstance(MainActivity.secondVectorQuantityString);
            if (newInstance.getDimension() > 0) {
                vectorQuantity.update(newInstance);
                Log.d(TAG, "send to client updateVectorQuantityOut secondVectorQuantity = " + vectorQuantity);
            }
        }

        @Override
        public void updateVectorQuantityInOut(VectorQuantity vectorQuantity) {
            sendBroadcastToActivity(vectorQuantity, 3);

            Log.d(TAG, "receive from client updateVectorQuantityOut thirdVectorQuantity = " + vectorQuantity);
            VectorQuantity newInstance = VectorQuantity.getNewInstance(MainActivity.thirdVectorQuantityString);
            if (newInstance.getDimension() > 0) {
                vectorQuantity.update(newInstance);
                Log.d(TAG, "send to client updateVectorQuantityOut thirdVectorQuantity = " + vectorQuantity);
            }
        }
    };

    private void sendBroadcastToActivity(VectorQuantity vectorQuantity, int number) { // 注释3
        Intent activity_intent = new Intent(UPDATE_EDIT_TEXT_ACTION);
        activity_intent.putExtra("number", number);
        activity_intent.putExtra("vector_quantity", vectorQuantity);
        activity_intent.setPackage(getPackageName());
        sendBroadcast(activity_intent);
    }

    public VectorQuantityService() {
    }

    @Override
    public IBinder onBind(Intent intent) { // 注释2
        // TODO: Return the communication channel to the service.
        return iVectorQuantityBinder;
    }
}
  1. 注释1:IVectorQuantityInterface.Stub()实现Stub代码,里面的操作是将客户端传过来的数据通过broadcast传给服务端MainActivity展现,然后进行修改;
  2. 注释2:返回服务端Ibinder
  3. 注释3:将客户端发送过来的数据通过Broadcast传递给MainActivity
5.2.2.3 MainActivity 代码

请参考 测试代码–AndroidIPCServer

5.2.3 客户端代码

客户端代码结构如下:

5.2.3.1 aidl 文件和自定义数据类代码

aidl文件和自定义数据类代码可以完全复制服务端代码,包括包名。

5.2.3.2 客户端调用aidl接口文件中定义的方法

代码5

com.ieening.androidipcclient.MainActivity

package com.ieening.androidipcclient;

......
   
import com.ieening.androidipcserver.vectorquantity.IVectorQuantityInterface;
import com.ieening.androidipcserver.vectorquantity.VectorQuantity;

public class MainActivity extends AppCompatActivity {
   private final static String TAG = MainActivity.class.getName();
   private ActivityMainBinding binding;

   private IVectorQuantityInterface vectorQuantityRemote = null;

   private final VectorQuantity firstVectorQuantity = new VectorQuantity(); // 注释1
   private final VectorQuantity secondVectorQuantity = new VectorQuantity();
   private final VectorQuantity thirdVectorQuantity = new VectorQuantity();


   private final ServiceConnection serviceConnection = new ServiceConnection() { // 注释2
       @Override
       public void onServiceConnected(ComponentName name, IBinder service) {
           vectorQuantityRemote = IVectorQuantityInterface.Stub.asInterface(service);
           Log.d(TAG, "executing serviceConnection onServiceConnected");
           changeVectorQuantityButtonsStatus();
       }

       @Override
       public void onServiceDisconnected(ComponentName name) {
           vectorQuantityRemote = null;
           Log.d(TAG, "executing serviceConnection onServiceDisconnected");
           changeVectorQuantityButtonsStatus();
       }
   };

   ......

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       binding = ActivityMainBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       ......

       setVectorQuantityNumberButtonsOnclickListener();

       ......
   }

  ......

   private void setVectorQuantityNumberButtonsOnclickListener() { // 注释3
       binding.vectorQuantityFirstNumberButton.setOnClickListener(v -> {
           try {
               Log.d(TAG, "send to server updateVectorQuantityIn firstVectorQuantity = " + firstVectorQuantity);
               vectorQuantityRemote.updateVectorQuantityIn(firstVectorQuantity);
               binding.firstNumberReceiveFromServerEditText.setText(firstVectorQuantity.toString());
               Log.d(TAG, "receive from server updateVectorQuantityIn firstVectorQuantity = " + firstVectorQuantity);
           } catch (RemoteException e) {
               e.printStackTrace();
               throw new RuntimeException(e);
           }
       });

       binding.vectorQuantitySecondNumberButton.setOnClickListener(v -> {
           try {
               Log.d(TAG, "send to server updateVectorQuantityOut secondVectorQuantity = " + secondVectorQuantity);
               vectorQuantityRemote.updateVectorQuantityOut(secondVectorQuantity);
               binding.secondNumberReceiveFromServerEditText.setText(secondVectorQuantity.toString());
               Log.d(TAG, "receive from server updateVectorQuantityOut secondVectorQuantity = " + secondVectorQuantity);
           } catch (RemoteException e) {
               e.printStackTrace();
               throw new RuntimeException(e);
           }
       });

       binding.vectorQuantityThirdNumberButton.setOnClickListener(v -> {
           try {
               Log.d(TAG, "send to server updateVectorQuantityInOut thirdVectorQuantity = " + thirdVectorQuantity);
               vectorQuantityRemote.updateVectorQuantityInOut(thirdVectorQuantity);
               binding.thirdNumberReceiveFromServerEditText.setText(thirdVectorQuantity.toString());
               Log.d(TAG, "receive from server updateVectorQuantityInOut thirdVectorQuantity = " + thirdVectorQuantity);
           } catch (RemoteException e) {
               e.printStackTrace();
               throw new RuntimeException(e);
           }
       });
   }

   ......
}
  1. 注释1:三个定向Tag在客户端对应的数据对象;
  2. 注释2:绑定服务端ServiceServiceConnection类;
  3. 注释3:调用aidl接口中的三个方法,将数据传递给服务端,并接收服务端传递过来的数据;

5.3 结果展示

5.3.1 软件界面介绍

aidl_different_application_server_client_ui

5.3.2 操作结果

从结果上看,完全符合4.1.2中预设的实验现象。

5.4 数据流实现细节

AIDL 文件最终生成.java 文件,因此需要在该文件里找答案。

代码6

com.ieening.androidipcserver.vectorquantity.IVectorQuantityInterface

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.ieening.androidipcserver.vectorquantity;
public interface IVectorQuantityInterface extends android.os.IInterface
{
    ......
    private static class Proxy implements com.ieening.androidipcserver.vectorquantity.IVectorQuantityInterface
    {
        
      ......
          
      /**
       * Demonstrates some basic types that you can use as parameters
       * and return values in AIDL.
       */
      @Override public void updateVectorQuantityIn(com.ieening.androidipcserver.vectorquantity.VectorQuantity vectorQuantity) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _Parcel.writeTypedObject(_data, vectorQuantity, 0);
          boolean _status = mRemote.transact(Stub.TRANSACTION_updateVectorQuantityIn, _data, _reply, 0);
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public void updateVectorQuantityOut(com.ieening.androidipcserver.vectorquantity.VectorQuantity vectorQuantity) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_updateVectorQuantityOut, _data, _reply, 0);
          _reply.readException();
          if ((0!=_reply.readInt())) {
            vectorQuantity.readFromParcel(_reply);
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public void updateVectorQuantityInOut(com.ieening.androidipcserver.vectorquantity.VectorQuantity vectorQuantity) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _Parcel.writeTypedObject(_data, vectorQuantity, 0);
          boolean _status = mRemote.transact(Stub.TRANSACTION_updateVectorQuantityInOut, _data, _reply, 0);
          _reply.readException();
          if ((0!=_reply.readInt())) {
            vectorQuantity.readFromParcel(_reply);
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
    }
    ......
  }
  ......
  /** @hide */
  static class _Parcel {
    static private <T> T readTypedObject(
        android.os.Parcel parcel,
        android.os.Parcelable.Creator<T> c) {
      if (parcel.readInt() != 0) {
          return c.createFromParcel(parcel);
      } else {
          return null;
      }
    }
    static private <T extends android.os.Parcelable> void writeTypedObject(
        android.os.Parcel parcel, T value, int parcelableFlags) {
      if (value != null) {
        parcel.writeInt(1);
        value.writeToParcel(parcel, parcelableFlags);
      } else {
        parcel.writeInt(0);
      }
    }
  }
}

如上代码所示,在Proxy中,

  1. in修饰的updateVectorQuantityIn函数中,在执行mRemote.transact之前,会将数据写入(_Parcel.writeTypedObject(_data, vectorQuantity, 0)),但没有将数据读出(vectorQuantity.readFromParcel(_reply));
  2. out修饰的updateVectorQuantityOut函数中,在执行mRemote.transact之前,不会将数据写入(_Parcel.writeTypedObject(_data, vectorQuantity, 0)),但会将数据读出(vectorQuantity.readFromParcel(_reply));
  3. inout修饰的updateVectorQuantityInOut函数中,在执行mRemote.transact之前,既有将数据写入(_Parcel.writeTypedObject(_data, vectorQuantity, 0)),也有将数据读出(vectorQuantity.readFromParcel(_reply));

测试代码:AndroidIPCClient 和 AndroidIPCServer

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值