【问题标题】:Callback in AIDL with multiple values具有多个值的 AIDL 中的回调
【发布时间】:2021-01-13 19:46:14
【问题描述】:

我想在aidl (aosp) android 中实现一个回调函数,它应该传输多个值而不是一个。如何解决?使用对对象?或者有没有其他更好的方法?

【问题讨论】:

  • 你的意思是返回多个值的回调吗?还是以多个值作为输入的回调?后一种情况似乎很简单,所以我假设你的意思是前者。您可以通过将标记为out 的参数添加到函数来返回多个值。这对我来说似乎比添加一个新的 pair 对象更容易。当然,如果您返回的值在某种程度上是紧密耦合的,那么从设计的角度来看,使用一种对对象是有意义的。
  • 是一个返回多个值的回调。是的,它们确实是耦合的。比如:一本书的“标题”和“内容”。
  • 好的,在这种情况下,您可以创建一个新的 Parcelable 类型。您的目标是什么语言(即 Java 或 C++)?对于 C++,您可以查看如何为 Java 创建自定义可打包 here,例如here.
  • 谢谢,我正在使用 java。
  • @f9c69e9781fa194211448473495534 你是否也知道一个简单的实现简单回调到aidl的简单示例。

标签: android android-source aidl


【解决方案1】:

这是一个用 Java 实现返回自定义对象的 AIDL 回调的示例。

接口规范

  • SomeResult.aidl

    您可以为要通过 binder 传输的自定义类定义 AIDL。

    package com.example;
    
    parcelable SomeResult;
    
  • ISomeCallback.aidl

    您的回调会有一个 AIDL。请注意,这会导入 parcelable 的先前 AIDL。

    package com.example;
    
    import com.example.SomeResult;
    
    interface ISomeCallback {
      SomeResult getResult();
    }
    
  • ISomeService.aidl

    您的服务会有一个 AIDL。您的服务可能有一些将回调作为输入的方法。当然,也可以有一个返回回调的方法,这取决于你想要做什么。

    package com.example;
    
    import com.example.ISomeCallback;
    
    interface ISomeService {
      void someAction(ISomeCallback callback);
    }
    

实施

然后您可以编写实现这些接口的 Java 类。

  • SomeResult.java

    parcelable 类型必须实现Parcelable 接口。您必须提供有关如何向/从Parcel 写入/读取自定义类的实现。通常这是通过将类的每个成员写入包裹然后以相同的顺序再次读取它们来完成的。

    package com.example;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class SomeResult implements Parcelable {
      private String someData1;
      private String someData2;
    
      public SomeResult(String someData1, String someData2) {
        this.someData1 = someData1;
        this.someData2 = someData2;
      }
    
      @Override
      public int describeContents() {
        return 0; // No special data, such as file descriptors
      }
    
      @Override
      public void writeToParcel(Parcel out, int flags) {
        out.writeString(someData1);
        out.writeString(someData2);
      }
    
      public static final Parcelable.Creator<SomeResult> CREATOR = new Parcelable.Creator() {
        public SomeResult createFromParcel(Parcel in) {
          return new SomeResult(in.readString(), in.readString());
        }
    
        public SomeResult[] newArray(int size) { return new SomeResult[size]; }
      };
    }
    
  • SomeCallback.java

    在您的回调中,您将构建自定义类的新实例并返回它。

    package com.example;
    
    import com.example.ISomeCallback;
    import com.example.SomeResult;
    
    public class SomeCallback extends ISomeCallback.Stub {
      @Override
      public SomeResult getResult() {
        return new SomeResult("Hello", "world!");
      }
    }
    
  • SomeService.java 您将实现接受回调的服务的方法。在该方法中,您可以调用回调来获取自定义类的实例。

    package com.example;
    
    import android.os.RemoteException;
    import com.example.ISomeService;
    
    public class SomeService extends ISomeService.Stub {
      @Override
      public void someAction(ISomeCallback callback) throws RemoteException {
        SomeResult result = callback.getResult();
    
        // ...
      }
    }
    

用法

要使用您的服务,您可以从服务管理器获取代理对象(假设您的服务作为系统服务运行,如果您的服务通过其他方式运行,您获取代理对象的方式会有所不同) .然后,您将构造一个回调类的实例,并使用您的回调作为参数调用服务方法:

    ISomeService someService = ISomeService.Stub.asInterface(
        ServiceManager.getService("some_service"));

    ISomeCallback someCallback = new SomeCallback();

    try {
        someService.someAction(someCallback);
    } catch (RemoteException ex) {
        // TODO Handle error
    }

【讨论】:

  • 非常感谢您的 sn-ps。我很感激。我不明白的是(也许我们彼此误解了):我想要一个监听器来监听来自服务的回调,而不需要我的客户要求它。想象以下场景:在系统服务中,有一个服务器正在运行,等待通过 tcp 的消息。每当消息到达时,它都会将其转发给已启动服务的客户端。
  • 也许上面的 sn-p 可以做到这一点,但看起来客户端必须主动调用方法并获取返回值。但我希望的是一个类似于 java 的侦听器模式。
  • 好的,我不知道你是如何启动服务的细节。您如何让服务了解客户?客户端和服务之间是否存在一对一的关系,还是一个服务会管理多个客户端?使用上面的示例代码,您不必让客户端请求每个回调调用。您也可以将 someAction 重命名为例如registerClient 并让该方法将回调存储在服务端的某个 List&lt;ICallback&gt; 中。当服务处理一个事件时,它可以遍历这个列表并触发该列表中的每个回调。
  • 是的,你确实理解我的意思。但是如何通知客户端触发的回调呢?因为我在客户端没有任何监听器来监听来自服务端的任何事件。
  • 到达服务的ICallback 是一个代理对象,它将所有通过 binder 的调用转发到原始实例。因此,当服务调用callback.getResult() 时,getResult 函数在客户端进程中运行(结果通过 binder 传回服务)。特别是,这意味着您可以从 getResult 的实现中修改客户端状态。
猜你喜欢
  • 1970-01-01
  • 2019-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多