为什么Android绑定服务文档表明IBinder不能跨进程使用?

时间:2017-07-17 11:28:20

标签: android ipc android-binder

来自https://developer.android.com/guide/components/bound-services.html

  

如果您的服务对您自己的应用程序是私有的并且在与客户端相同的进程中运行(这是常见的),您应该通过扩展Binder类并从onBind()返回它的实例来创建您的接口。客户端接收Binder并可以使用它直接访问Binder实现或服务中可用的公共方法。

     

当您的服务仅仅是您自己的应用程序的后台工作程序时,这是首选技术。您不会以这种方式创建界面的唯一原因是因为您的服务被其他应用程序或单独的进程使用。

     

如果您需要在不同流程中使用界面,可以使用Messenger为服务创建界面...

这似乎意味着你不会使用Binder进行进程间通信,但IBinder documentation反之亦然:

  

远程对象的基本接口,是轻量级远程过程调用机制的核心部分,旨在实现进程内和跨进程调用时的高性能。此接口描述了与远程对象交互的抽象协议。不要直接实现此接口,而是从Binder扩展。

     

关键的IBinder API是由Binder.onTransact()匹配的transact()。这些方法允许您分别向IBinder对象发送调用并接收进入Binder对象的调用。此事务API是同步的,因此在从Binder.onTransact()返回目标之前,不会返回对transact()的调用。这是调用本地进程中存在的对象时的预期行为,并且底层进程间通信(IPC)机制确保在跨进程时应用这些相同的语义。

Binder documentation

  

但是,您可以直接从Binder派生来实现您自己的自定义RPC协议,或者直接实例化原始Binder对象以用作可以跨进程共享的令牌。

     

这个类只是一个基本的IPC原语......

许多其他页面也提到IPC上下文中的绑定器。我误解了什么?

2 个答案:

答案 0 :(得分:1)

IBinder为远程对象提供基本界面Binder是IBinder的一个实现,它提供了标准支持,可以创建远程对象的本地实现。

当服务和客户端都在同一个进程中运行时,只需扩展Binder并返回Binder子类的实例即可通过绑定到服务来与服务进行通信,因为Binder是IBinder的一个实现,它提供了创建远程对象的本地实现的标准支持。

当服务在单独的进程中运行并且服务的客户端可以在任何其他进程中运行时,我们需要实现IBinder并提供为远程实现远程实现提供支持的实现。这可以通过以下两种方式之一来实现:

使用AIDL

使用AIDL,我们可以编写一个我们想要公开的接口。 aidl工具然后可以解析此文件并自动生成Stub和一些锅炉板代码,这对于IPC的所有应用程序是通用的。然后我们可以扩展Interface.Stub以在服务端提供实现。

客户可以在其项目中包含此AIDL文件,IPC的代码由aidl工具自动生成。现在,客户端可以绑定到您的服务并使用AIDL文件中声明的接口进行通信。

使用Messenger

Messenger是对Handler的引用,其他人可以使用它来向其发送消息。这允许跨进程实现基于消息的通信,方法是在一个进程中创建指向Handler的Messenger,并将该Message传递给另一个进程。该IPC在内部使用AIDL本身实现。 HereIMessenger.aidl接口,Messenger在内部使用该接口来公开客户可用于与主机进程通信的合同。

<强>摘要

IBinder可用于进程内和进程间通信。将其用于进程间通信时,只需使用AIDL公开接口。

答案 1 :(得分:0)

我在IPC上遇到问题已有几天了。但是在pskink的慈善帮助下,我学到了很多东西。

我讨厌AIDL。我讨厌这个词。为什么当这实际上只是一些实现onTransact并调用transact的java类时,才使用这种奇怪的新术语。没必要,而且比您自己动手更难实施!

因此调用AIDL接口:

Print.aidl

interface Print 
{
  void print(String s);
}

请原谅我的帮助代码。 AIDL编译器将其编译为Java文件。

Print.java

// note that AIDL takes care to use fully qualified names
public interface Print
{
     void print(String s);

     // This is used for local process calls
     public abstract static class Stub extends Binder
                                                               implements Print
     {
          private Stub()
          {
            attachInterface(this);
          }

          public static Print asInterface(IBinder binder)
          {
           return binder == null ? null :
                      binder instanceof Print.Stub ? new Print.Stub() :
                        /* else */           new Print.Stub.Proxy(binder);
          }

          @Override public IBinder asBinder { return this; }

          @Override public boolean onTransact(int event,Parcel out,Parcel reply,int flags)
          {
               switch(event)
               {
                 case TRANSACTION_print: // print data.readString();
               }
          }

          // This is used for IPC
          private static class Proxy implements Print
          {
               IBinder binder;

               @Override public void print(String s)
               {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                data.writeString(s);
                binder.transact(TRANSACTION_print,data,reply,0);
                reply.readException();
               }
          }
     }
}

这几乎是AIDL生成的代码,并且从未向您显示。我不知道为什么它与JIDL,PIEL或TBHJ不同。但是,我们不需要AIDL工具和上面的所有代码都可以简化!

process1 / Print.java

public class Print extends Binder
{
     public void print(String s) { // print string }

          @Override public boolean onTransact(int event,Parcel out,Parcel reply,int flags)
          {
               switch(event)
               {
                 case TRANSACTION_print: // print data.readString();
               }
          }
}

process2 / Print

public class Print
{
     public void print(String s)
     {
      Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                data.writeString(s);
                binder.transact(TRANSACTION_print,data,reply,0);
                reply.readException();
     }
}

如您所见,它很简单。唯一剩下的就是使用局部功能而不是流程代理的决定。这很容易实现。

请注意,绑定程序不适用于IPC句号...它们仅适用于绑定服务到客户端的通信。试图在两个活动之间进行交易似乎可行。正如pskink指出的那样。

要在两个活动之间进行通信,可以使用Messenger API。据说Messenger只是在使用活页夹,但我认为这是事实的一半。正如我所说,使用活页夹在两个活动之间进行通信会失败!顺便说一句,活页夹是内核驱动程序。因此,我猜想Messenger所使用的资料夹是一种特殊的内核资料夹驱动程序!

另一种通信方式是通过ParcelFileDescriptor.createPipe()!!!宝贝Android支持管道。问题是,通过意图将它们编组是一个致命的失败,应用程序会严重崩溃。管道再次出现,似乎仅用于服务到活动的通信。

最后。无法使用活页夹进行聊天真是荒谬的事情。不同过程中的两个活动。无法将可打包描述符放入意图中。您必须遵循所设定的教义。更糟的是,糟糕的文档。