I'm attempting to build a capability that allows a ContentProvider
to extract a thumbnail from a raw image.
Here is the implementation:
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException
{
[Uri handling stuff]
return openPipeHelper(image, "unused", null, null, this);
}
@Override
public void writeDataToPipe(@NonNull ParcelFileDescriptor output, @NonNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable Void args)
{
final String where = Meta.URI_SELECTION;
final String[] whereArgs = new String[]{uri.toString()};
Meta.ImageType imageType = null;
try(Cursor metaCursor = query(
Meta.META_URI, new String[] { Meta.TYPE },
where, whereArgs, null, null))
{
if (metaCursor != null && metaCursor.moveToFirst())
imageType = Meta.ImageType.fromInt(metaCursor.getInt(metaCursor.getColumnIndex(Meta.TYPE)));
}
byte[] thumbData;
//noinspection ConstantConditions
try(AssetFileDescriptor fd = getContext().getContentResolver().openAssetFileDescriptor(uri, "r"))
{
if (imageType == null || imageType == Meta.ImageType.UNPROCESSED)
{
imageType = ImageUtil.getImageType(getContext(), uri);
// Update the image type
ContentValues value = new ContentValues(1);
value.put(Meta.TYPE, imageType.getValue());
update(Meta.META_URI, value, where, whereArgs);
}
switch(imageType)
{
case TIFF:
thumbData = ImageUtil.getTiffImage(fd.getParcelFileDescriptor().getFd());
break;
default:
thumbData = ImageUtil.getRawThumb(fd.getParcelFileDescriptor().getFd());
break;
}
}
catch (IOException e)
{
Log.e(TAG, ": Failed to open: " + uri, e);
return;
}
if (thumbData == null)
{
Log.e(TAG, ": Failed to extract: " + uri);
return;
}
try (FileOutputStream fout = new FileOutputStream(output.getFileDescriptor()))
{
fout.write(thumbData, 0, thumbData.length); // FAILS HERE
Log.d(TAG, "--End openFile: " + uri);
}
catch (IOException e)
{
Log.e(TAG, "*Fail openFile: " + uri, e);
}
}
The code used a PipeDataWriter
to place a byte[] from a native (jni) decoded thumbnail ImageUtil.getRawThumb
into the read side of a pipe fout.write(thumbData, 0, thumbData.length)
that will feed openFile
.
However, when I load a few hundred images I'm finding 5-10% of images fail with errors similar to:
03-24 23:34:49.022 26890-28130/com.anthonymandra.rawdroidpro E/MetaProvider: *Fail openFile: content://com.android.externalstorage.documents/tree/0000-0000%3A_largeSet/document/0000-0000%3A_largeSet%2FKyoto-255.CR2 java.io.IOException: write failed: EPIPE (Broken pipe) at libcore.io.IoBridge.write(IoBridge.java:498) at java.io.FileOutputStream.write(FileOutputStream.java:186) at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:270) at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:40) at android.content.ContentProvider$1.doInBackground(ContentProvider.java:1712) at android.os.AsyncTask$2.call(AsyncTask.java:295) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818) Caused by: android.system.ErrnoException: write failed: EPIPE (Broken pipe) at libcore.io.Posix.writeBytes(Native Method) at libcore.io.Posix.write(Posix.java:271) at libcore.io.BlockGuardOs.write(BlockGuardOs.java:313) at libcore.io.IoBridge.write(IoBridge.java:493) at java.io.FileOutputStream.write(FileOutputStream.java:186) at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:270) at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:40) at android.content.ContentProvider$1.doInBackground(ContentProvider.java:1712) at android.os.AsyncTask$2.call(AsyncTask.java:295) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818)
ContentResolver.openInputStream
is being called by Glide, which is using up to 'core-count' threads to load images.
Previously I just loaded the thumbnail via a custom loader for Glide without issue. I'm guessing there's some concurrency limitation to the underlying "pipe" system. Most questions on this topic don't get a satisfying answer. Is there anything obviously wrong here or am I exceeding the capabilities of the system?