Ich könnte diese Datei auf die Festplatte schreiben und sie dann einfach übergeben Ergebnis von ParcelFileDescriptor.open(File) an die anfordernde App, aber dies hinterlässt ein Artefakt auf der Festplatte, bei dem es sich praktisch um eine redundante Kopie handelt, die schnell veraltet. Ich kenne auch keinen Mechanismus, mit dem ich diese Datei entfernen kann, sobald sie vollständig verbraucht ist. Daher müsste ich einen Mechanismus implementieren, der diese Dateien schließlich löscht.
Stattdessen möchte ich einen Puffer direkt übergeben, ohne zunächst auf die Festplatte schreiben zu müssen. Am Ende habe ich ParcelFileDescriptor.createPipe() verwendet, um den Inhalt mithilfe von Eingabe-/Ausgabeströmen und einem zusätzlichen Thread direkt zu streamen. Dies scheint in den meisten Fällen gut zu funktionieren, aber von allen Apps scheint Gmail den InputStream sofort zu schließen, was zu einer Ausnahme wegen unterbrochener Pipe führt und Gmail sich über eine leere Datei beschwert:
< pre class="lang-java Prettyprint-override">
Code: Select all
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
long id = Long.parseLong(Objects.requireNonNull(uri.getPathSegments().get(0)));
try {
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
ParcelFileDescriptor output = pipe[1];
new Thread(() -> {
try (ParcelFileDescriptor.AutoCloseOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(output)) {
serializeZipFile(out, id);
} catch (IOException e) {
Log.e(TAG, "Error during zip serialization.", e);
}
}).start();
return pipe[0];
} catch (IOException e) {
Log.e(TAG, "Error while trying to create pipe", e);
throw new FileNotFoundException("Failed to create pipe");
}
}
Code: Select all
StorageManager storageManager = Objects.requireNonNull(getContext()).getSystemService(StorageManager.class);
ByteArrayOutputStream out = new ByteArrayOutputStream();
serializeZipFile(out, id);
return storageManager.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, new ProxyFileDescriptorCallback() {
private final byte[] bytes = out.toByteArray();
@Override
public long onGetSize() {
return bytes.length;
}
@Override
public int onRead(long offset, int size, byte[] data) {
if (offset >= bytes.length) {
return 0;
}
int intOffset = Math.toIntExact(offset);
int realSize = Math.min(Math.min(size, bytes.length - intOffset), data.length);
System.arraycopy(bytes, intOffset, data, 0, realSize);
return realSize;
}
@Override
public void onRelease() {}
}, new Handler(Looper.getMainLooper()));