gpt4 book ai didi

android - 使用 ParcelFileDescriptor.createPipe() 将 InputStream 传输到另一个服务(跨进程边界)失败,返回 "EBADF (Bad file number)"

转载 作者:塔克拉玛干 更新时间:2023-11-01 21:42:01 27 4
gpt4 key购买 nike

我想通过使用 ParcelFileDescriptor.createPipe() 将 InputStream 从一个 Android 服务“发送”到在不同进程中运行的另一个服务。 ,一个流到流的复制线程和一个 ParcelFileDescriptor,代表管道的读取端,它通过 Binder IPC 提供给其他服务。

发送代码(流程A)

我想将给定的 InputStream 发送到接收服务:

public sendInputStream() {
InputStream is = ...; // that's the stream for process/service B
ParcelFileDescriptor pdf = ParcelFileDescriptorUtil.pipeFrom(is);
inputStreamService.inputStream(pdf);
}

ParcelFileDescriptorUtil 是一个辅助类,具有经典的 java.io. 流到流复制线程:

public class ParcelFileDescriptorUtil {

public static ParcelFileDescriptor pipeFrom(InputStream inputStream) throws IOException {
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
ParcelFileDescriptor readSide = pipe[0];
ParcelFileDescriptor writeSide = pipe[1];

// start the transfer thread
new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)).start();

return readSide;
}

static class TransferThread extends Thread {
final InputStream mIn;
final OutputStream mOut;

TransferThread(InputStream in, OutputStream out) {
super("ParcelFileDescriptor Transfer Thread");
mIn = in;
mOut = out;
setDaemon(true);
}

@Override
public void run() {
byte[] buf = new byte[1024];
int len;

try {
while ((len = mIn.read(buf)) > 0) {
mOut.write(buf, 0, len);
}
mOut.flush(); // just to be safe
} catch (IOException e) {
LOG.e("TransferThread", e);
}
finally {
try {
mIn.close();
} catch (IOException e) {
}
try {
mOut.close();
} catch (IOException e) {
}
}
}
}
}

接收服务码(流程B)

接收服务的.aidl:

package org.exmaple;
interface IInputStreamService {
void inputStream(in ParcelFileDescriptor pfd);
}

进程A调用的接收服务:

public class InputStreamService extends Service {

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

private final IInputStreamService.Stub mBinder = new IInputStreamService.Stub() {

@Override
public void inputStream(ParcelFileDescriptor pfd) throws RemoteException {

InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
OutputStream os = ...;
int len;
byte[] buf = new byte[1024];
try {
while ((len = is.read(buf)) > 0) {
os.write(buf, 0, len);
}
} catch (IOException e) {
// this catches the exception shown below
}
}
};

但是 inputStream() 中的 in.read() 总是抛出一个 IOException

java.io.IOException: read failed: EBADF (Bad file number)
at libcore.io.IoBridge.read(IoBridge.java:442)
at java.io.FileInputStream.read(FileInputStream.java:179)
at java.io.InputStream.read(InputStream.java:163)

似乎 EBADF errno 是在文件描述符关闭时由 read() 设置的。但我不知道是什么原因造成的,也不知道如何解决。

是的,我知道也可以使用 ConentProvider。但它不应该也适用于我的方法吗?有没有其他方法可以将 InputStream 流传递给 Android 中的不同服务?

旁注:CommonsWare 创建了一个类似的 project using a ContentProvider (相关 SO 问题 12 )。这是我从中获得大部分方法想法的地方

最佳答案

看起来原因是 ParcelFileDescriptor 是服务方法的参数。如果该服务确实返回 ParcelFileDescriptor,它将按预期工作。

发送服务(进程A)

public void sendInputStream() {
InputStream is = ...; // that's the stream for process/service B
ParcelFileDescriptor pfd = inputStreamService.inputStream();
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);

int len;
byte[] buf = new byte[1024];
try {
while ((len = is.read(buf)) > 0) {
os.write(buf, 0, len);
}
} catch (IOException e) {
} finally {
try { is.close(); } catch (IOException e1) {}
try { os.close(); } catch (IOException e1) {}
}
}

接收服务码(流程B)

接收服务的.aidl:

package org.exmaple;
interface IInputStreamService {
ParcelFileDescriptor inputStream();
}

进程A调用的接收服务:

public class InputStreamService extends Service {

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

private final IInputStreamService.Stub mBinder = new IInputStreamService.Stub() {

@Override
public void ParcelFileDescriptor inputStream() throws RemoteException {
// one can read the contents of the Processes A's InputStream
// from the following OutputStream
OutputStream os = ...;
ParcelFileDescriptor pfd = ParcelFileDescriptorUtil.pipeTo(os);
return pfd;
}
};

ParcelFileDescriptorUtil 是一个辅助类,具有经典的 java.io. 流到流复制线程。 现在我们必须使用 pipeTo() 方法

public class ParcelFileDescriptorUtil {

public static ParcelFileDescriptor pipeTo(OutputStream outputStream) throws IOException {
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
ParcelFileDescriptor readSide = pipe[0];
ParcelFileDescriptor writeSide = pipe[1];

// start the transfer thread
new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream).start();

return writeSide;
}

static class TransferThread extends Thread {
final InputStream mIn;
final OutputStream mOut;

TransferThread(InputStream in, OutputStream out) {
super("ParcelFileDescriptor Transfer Thread");
mIn = in;
mOut = out;
setDaemon(true);
}

@Override
public void run() {
byte[] buf = new byte[1024];
int len;

try {
while ((len = mIn.read(buf)) > 0) {
mOut.write(buf, 0, len);
}
mOut.flush(); // just to be safe
} catch (IOException e) {
LOG.e("TransferThread", e);
}
finally {
try {
mIn.close();
} catch (IOException e) {
}
try {
mOut.close();
} catch (IOException e) {
}
}
}
}
}

这允许您跨进程边界传输 InputStreams,一个缺点是流到流的复制涉及一些 CPU 时间。

关于android - 使用 ParcelFileDescriptor.createPipe() 将 InputStream 传输到另一个服务(跨进程边界)失败,返回 "EBADF (Bad file number)",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18212152/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com