Эта явно раздражающая ситуация возникает из-за того, что при построении .apk
некоторые ресурсы сжимаются перед их сохранением, тогда как другие обрабатываются как уже сжатые (например, изображения, видео) и остаются одни.Последняя группа может быть открыта с помощью openAssetFd
, первая группа не может - если вы попытаетесь, вы получите сообщение об ошибке «Этот файл нельзя открыть как дескриптор файла; возможно, он сжат».
Один из вариантов - заставить систему сборки не сжимать ресурсы (см. Ссылку в ответе @ nicstrong), но это довольно сложно.Лучше попытаться обойти проблему более предсказуемым образом.
Решение, с которым я столкнулся, использует тот факт, что, хотя вы не можете открыть AssetFileDescriptor
для актива, вы все равно можете открыть InputStream
.Вы можете использовать это, чтобы скопировать ресурс в файловый кеш приложения, а затем вернуть дескриптор для этого:
@Override
public AssetFileDescriptor openAssetFile(final Uri uri, final String mode) throws FileNotFoundException
{
final String assetPath = uri.getLastPathSegment(); // or whatever
try
{
final boolean canBeReadDirectlyFromAssets = ... // if your asset going to be compressed?
if (canBeReadDirectlyFromAssets)
{
return getContext().getAssets().openFd(assetPath);
}
else
{
final File cacheFile = new File(getContext().getCacheDir(), assetPath);
cacheFile.getParentFile().mkdirs();
copyToCacheFile(assetPath, cacheFile);
return new AssetFileDescriptor(ParcelFileDescriptor.open(cacheFile, MODE_READ_ONLY), 0, -1);
}
}
catch (FileNotFoundException ex)
{
throw ex;
}
catch (IOException ex)
{
throw new FileNotFoundException(ex.getMessage());
}
}
private void copyToCacheFile(final String assetPath, final File cacheFile) throws IOException
{
final InputStream inputStream = getContext().getAssets().open(assetPath, ACCESS_BUFFER);
try
{
final FileOutputStream fileOutputStream = new FileOutputStream(cacheFile, false);
try
{
//using Guava IO lib to copy the streams, but could also do it manually
ByteStreams.copy(inputStream, fileOutputStream);
}
finally
{
fileOutputStream.close();
}
}
finally
{
inputStream.close();
}
}
Это означает, что ваше приложение оставит файлы кеша лежащими, но это нормально.Он также не пытается повторно использовать существующие файлы кэша, которые могут вас не интересовать.