Перенесите InputStream на другую службу (через границы процесса) с помощью ParcelFileDescriptor.createPipe (), которая не работает с «EBADF (Bad file number)»

Я хочу «отправить» InputStream из одной службы Android в другую службу, запущенную в другом процессе, с помощью ParcelFileDescriptor.createPipe() , потокового потока потока и потока 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. Stream-to-stream copy Тема:

 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 } } }; 

Но in.read() в inputStream() всегда выдает inputStream() 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) 

Похоже, что errno EBADF задается read() когда дескриптор файла закрыт. Но я не знаю, что вызывает это и как это исправить.

И да, я знаю, что ConentProvider также будет возможностью. Но разве он не должен работать с моим подходом? Есть ли другие способы передать поток InputStream для другой службы на Android?

На стороне примечания: CommonsWare создал аналогичный проект, используя ContentProvider (связанные вопросы SO 1 , 2 ). Именно там я получил большинство идей для своего подхода

Похоже, причиной был ParcelFileDescriptor являющийся аргументом метода службы. Если служба возвращает ParcelFileDescriptor она работает ParcelFileDescriptor образом.

Служба отправки (процесс A)

 public 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. Stream-to-stream copy Thread. Теперь мы должны использовать метод 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 через границы процессов, один из недостатков заключается в том, что в копиях потокового потока присутствует некоторое время процессора.