为什么 Android Bound Services 文档提示 IBinder 不能跨进程使用?
Why does Android Bound Services documentation suggest IBinder can't be used across processes?
来自https://developer.android.com/guide/components/bound-services.html:
If your service is private to your own application and runs in the same process as the client (which is common), you should create your interface by extending the Binder class and returning an instance of it from onBind(). The client receives the Binder and can use it to directly access public methods available in either the Binder implementation or the Service.
This is the preferred technique when your service is merely a background worker for your own application. The only reason you would not create your interface this way is because your service is used by other applications or across separate processes.
If you need your interface to work across different processes, you can create an interface for the service with a Messenger...
这似乎暗示您不会使用 Binder 进行进程间通信,但IBinder
documentation却恰恰相反:
Base interface for a remotable object, the core part of a lightweight remote procedure call mechanism designed for high performance when performing in-process and cross-process calls. This interface describes the abstract protocol for interacting with a remotable object. Do not implement this interface directly, instead extend from Binder.
The key IBinder API is transact() matched by Binder.onTransact(). These methods allow you to send a call to an IBinder object and receive a call coming in to a Binder object, respectively. This transaction API is synchronous, such that a call to transact() does not return until the target has returned from Binder.onTransact(); this is the expected behavior when calling an object that exists in the local process, and the underlying inter-process communication (IPC) mechanism ensures that these same semantics apply when going across processes.
You can, however, derive directly from Binder to implement your own custom RPC protocol or simply instantiate a raw Binder object directly to use as a token that can be shared across processes.
This class is just a basic IPC primitive...
许多其他页面也提到了 IPC 上下文中的绑定器。我误会了什么?
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 本身在内部实现的。 Here 是 IMessenger.aidl
接口,Messenger
在内部使用它来公开客户可以用来与主机进程通信的合同。
总结
IBinder
可用于进程内和进程间通信。使用它进行进程间通信时,只需使用AIDL暴露接口即可。
几天来我一直在使用 IPC 遇到问题。但是在 pskink 的慈善援助下,我学到了很多东西。
一方面,我讨厌 AIDL。我讨厌这个词。为什么这个奇怪的新术语实际上只是一些 java 类 实现 onTransact 和调用交易。没有必要,而且比自己动手更难实现!
所以调用AIDL接口:
Print.aidl
interface Print
{
void print(String s);
}
原谅我的aidl代码。然而,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 只是在使用活页夹,但我认为这只对了一半。正如我所说,使用活页夹在两个活动之间进行通信会失败!顺便说一下,Binder 是一个内核驱动程序。所以我猜Messenger使用的binder是一个特殊的内核binder驱动!
另一种沟通方式是通过 ParcelFileDescriptor.createPipe()!!!!是的宝贝。 Android 支持管道。问题是,通过意图来编组它们是一个彻底的失败,应用程序会严重崩溃。再次使用管道,似乎只为 activity 通信服务。
最后。不能使用活页夹来聊天 up.two 不同进程中的活动,这是多么荒谬的事情。无法将可打包的描述符放入意图中。你必须只遵循强加给你的教义。更糟糕的是,糟糕的文档。
来自https://developer.android.com/guide/components/bound-services.html:
If your service is private to your own application and runs in the same process as the client (which is common), you should create your interface by extending the Binder class and returning an instance of it from onBind(). The client receives the Binder and can use it to directly access public methods available in either the Binder implementation or the Service.
This is the preferred technique when your service is merely a background worker for your own application. The only reason you would not create your interface this way is because your service is used by other applications or across separate processes.
If you need your interface to work across different processes, you can create an interface for the service with a Messenger...
这似乎暗示您不会使用 Binder 进行进程间通信,但IBinder
documentation却恰恰相反:
Base interface for a remotable object, the core part of a lightweight remote procedure call mechanism designed for high performance when performing in-process and cross-process calls. This interface describes the abstract protocol for interacting with a remotable object. Do not implement this interface directly, instead extend from Binder.
The key IBinder API is transact() matched by Binder.onTransact(). These methods allow you to send a call to an IBinder object and receive a call coming in to a Binder object, respectively. This transaction API is synchronous, such that a call to transact() does not return until the target has returned from Binder.onTransact(); this is the expected behavior when calling an object that exists in the local process, and the underlying inter-process communication (IPC) mechanism ensures that these same semantics apply when going across processes.
You can, however, derive directly from Binder to implement your own custom RPC protocol or simply instantiate a raw Binder object directly to use as a token that can be shared across processes.
This class is just a basic IPC primitive...
许多其他页面也提到了 IPC 上下文中的绑定器。我误会了什么?
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 本身在内部实现的。 Here 是 IMessenger.aidl
接口,Messenger
在内部使用它来公开客户可以用来与主机进程通信的合同。
总结
IBinder
可用于进程内和进程间通信。使用它进行进程间通信时,只需使用AIDL暴露接口即可。
几天来我一直在使用 IPC 遇到问题。但是在 pskink 的慈善援助下,我学到了很多东西。
一方面,我讨厌 AIDL。我讨厌这个词。为什么这个奇怪的新术语实际上只是一些 java 类 实现 onTransact 和调用交易。没有必要,而且比自己动手更难实现!
所以调用AIDL接口:
Print.aidl
interface Print
{
void print(String s);
}
原谅我的aidl代码。然而,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 只是在使用活页夹,但我认为这只对了一半。正如我所说,使用活页夹在两个活动之间进行通信会失败!顺便说一下,Binder 是一个内核驱动程序。所以我猜Messenger使用的binder是一个特殊的内核binder驱动!
另一种沟通方式是通过 ParcelFileDescriptor.createPipe()!!!!是的宝贝。 Android 支持管道。问题是,通过意图来编组它们是一个彻底的失败,应用程序会严重崩溃。再次使用管道,似乎只为 activity 通信服务。
最后。不能使用活页夹来聊天 up.two 不同进程中的活动,这是多么荒谬的事情。无法将可打包的描述符放入意图中。你必须只遵循强加给你的教义。更糟糕的是,糟糕的文档。