Transferir InputStream a otro servicio (a través de límites de proceso) con ParcelFileDescriptor.createPipe () falla con "EBADF (número de archivo incorrecto)"
Quiero "enviar" un InputStream de un servicio de Android a otro servicio que se ejecuta dentro de un proceso diferente utilizando ParcelFileDescriptor.createPipe()
, un flujo de flujo de secuencia a secuencia y un ParcelFileDescriptor, que representa el lado de lectura del canal, que es Dado al otro servicio con medios de Binder IPC.
Código de envío (proceso A)
Quiero enviar un InputStream dado al servicio de recepción:
- Android: startActivityForResult no llamando aActivityResult
- BindService () devuelve false pero unbindService () necesita ser llamado?
- Handler en Android
- ¿Cómo transferir archivos entre aplicaciones de Android que se ejecutan en el mismo dispositivo?
- Cómo obtener datos de un servicio sin consolidar en Android
public sendInputStream() { InputStream is = ...; // that's the stream for process/service B ParcelFileDescriptor pdf = ParcelFileDescriptorUtil.pipeFrom(is); inputStreamService.inputStream(pdf); }
El ParcelFileDescriptorUtil es una clase auxiliar, con un clásico java.io.
Stream-to-stream copy Hilo:
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) { } } } } }
Código de Servicio de Recepción (Proceso B)
El servicio de recepción .aidl
:
package org.exmaple; interface IInputStreamService { void inputStream(in ParcelFileDescriptor pfd); }
El servicio receptor, llamado por el Proceso 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 } } };
Pero in.read()
en inputStream()
siempre lanza una 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)
Parece que el errdo de EBADF está establecido por read()
cuando el descriptor de archivo está cerrado. Pero no sé qué lo está causando y cómo solucionarlo.
Y sí, sé que un ConentProvider también sería una posibilidad. Pero, ¿no debería funcionar también con mi enfoque? ¿Hay otras maneras de entregar un flujo de InputStream a un servicio diferente en Android?
En una nota lateral: CommonsWare creó un proyecto similar usando un ContentProvider (relacionadas SO preguntas 1 , 2 ). Es donde obtuve la mayoría de las ideas para mi enfoque de
- Cómo pasar un socket Bluetooth a otra actividad mediante la interfaz de la aplicación
- ¿Se puede utilizar Messenger (alternativa ligera a AIDL) para la comunicación entre aplicaciones?
- Android aidl no puede enlazar al servicio, cuando el host se instala después de que el cliente
- ¿Por qué utilizar parcelable cuando se puede realizar la misma tarea utilizando variables estáticas?
- ¿Cómo puedo notificar una actividad en ejecución de un receptor de difusión?
- Rendimiento de Android ContentProvider
- Lanzar una excepción personalizada de un servicio a una actividad
- ¿Qué ocurre cuando la actividad se bloquea?
Parece que la causa era el ParcelFileDescriptor
siendo un argumento del método de servicio. Si el servicio devuelve ParcelFileDescriptor
funciona como se esperaba.
Servicio de envío (proceso 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) {} } }
Código de Servicio de Recepción (Proceso B)
El servicio de recepción .aidl
:
package org.exmaple; interface IInputStreamService { ParcelFileDescriptor inputStream(); }
El servicio receptor, llamado por el Proceso 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; } };
El ParcelFileDescriptorUtil es una clase auxiliar, con un clásico java.io.
Secuencia-a-corriente copia Hilo. Ahora tenemos que usar el método 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) { } } } } }
Esto le permite transferir InputStreams a través de los límites del proceso, un inconveniente es que hay algún tiempo de CPU involucrado en las copias de flujo a flujo.
- Pregunta teórica: cargar el archivo XML-Layout externo en Android
- Cómo sincronizar datos entre dispositivos en Wi-Fi