je programme une table d'harmonie à partir d'Android. Le problème est que certains sons fonctionnent et que certains ne fonctionnent pas. Voici le suivi que je reçois pour les sons qui ne fonctionnent pas.
05-31 13:23:04.227 18440 18603 W System.err: Java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed
05-31 13:23:04.227 18440 18603 W System.err: at Android.content.res.AssetManager.openAssetFd(Native Method)
05-31 13:23:04.227 18440 18603 W System.err: at Android.content.res.AssetManager.openFd(AssetManager.Java:331)
05-31 13:23:04.227 18440 18603 W System.err: at com.phonegap.AudioPlayer.startPlaying(AudioPlayer.Java:201)
05-31 13:23:04.227 18440 18603 W System.err: at com.phonegap.AudioHandler.startPlayingAudio(AudioHandler.Java:181)
05-31 13:23:04.235 18440 18603 W System.err: at com.phonegap.AudioHandler.execute(AudioHandler.Java:64)
05-31 13:23:04.235 18440 18603 W System.err: at com.phonegap.api.PluginManager$1.run(PluginManager.Java:86)
05-31 13:23:04.235 18440 18603 W System.err: at Java.lang.Thread.run(Thread.Java:1096)
des idées?
L'ouverture de fichiers compressés dans le dossier des actifs est limitée. En effet, les fichiers non compressés peuvent être directement mappés en mémoire dans l'espace d'adressage virtuel du processus, évitant ainsi d'avoir à nouveau besoin de la même quantité de mémoire pour la décompression.
Traitement de la compression des actifs dans les applications Android explique certaines techniques de traitement des fichiers compressés. Vous pouvez tromper aapt
en ne compressant pas le fichier en utilisant une extension non compressée (par exemple mp3
) ou vous pouvez les ajouter manuellement à la apk
sans compression au lieu d'obtenir aapt
pour faire le travail.
Vous pouvez désactiver la compression des actifs pour certaines extensions, comme ceci:
Android {
aaptOptions {
noCompress "pdf"
}
}
Cette situation décidément irritante s’explique par le fait que, lorsque le .apk
est créé, certains actifs sont compressés avant de les stocker, tandis que d’autres sont traités comme déjà compressés (images, vidéo, par exemple) et sont laissés seuls. Le dernier groupe peut être ouvert en utilisant openAssetFd
, le premier groupe ne peut pas - si vous essayez, vous obtenez l'erreur "Ce fichier ne peut pas être ouvert en tant que descripteur de fichier; il est probablement compressé".
Une option consiste à tromper le système de compilation en ne compressant pas les actifs (voir le lien dans la réponse de @ nicstrong), mais c'est fastidieux. Mieux vaut essayer de contourner le problème de manière plus prévisible.
La solution que j'ai choisie utilise le fait que, bien que vous ne puissiez pas ouvrir une AssetFileDescriptor
pour l'actif, vous pouvez quand même ouvrir une InputStream
. Vous pouvez utiliser ceci pour copier l'actif dans le cache de fichiers de l'application, puis renvoyer un descripteur pour cela:
@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();
}
}
Cela signifie que votre application laissera des fichiers de cache en suspens, mais ce n'est pas grave. Il n'essaie pas non plus de réutiliser des fichiers de cache existants, ce qui peut vous intéresser.
Vous ne devriez obtenir cette exception que si vous essayez d'ouvrir la FileDesriptor
. Pour simplement lire le fichier, vous pouvez parcourir la InputStream
(AssetManager.open("filename.ext")
). Cela a fonctionné pour moi.
Si vous avez besoin de la taille du fichier à l'avance, vous avez besoin de la variable FileDescriptor
(et donc d'un fichier non compressé) pour appeler sa méthode getLength()
. Sinon, vous devez lire le flux entier pour déterminer sa taille.
Cette exception peut être levée en appelant:
final AssetFileDescriptor afd = activity.getAssets().openFd(path);
J'ai résolu le problème en enregistrant le fichier dans le répertoire res/raw
au lieu du dossier assets, puis j'ai obtenu la variable AssetFileDescriptor
de cette façon:
final AssetFileDescriptor afd = activity.getResources().openRawResourceFd(rawId);
Ensuite, la FileNotFoundException
a disparu et le fichier n'est plus compressé.
J'ai fait un tour, j'utilise:
ParcelFileDescriptor mFileDescriptor = context.getAssets().openFd(file).getParcelFileDescriptor();
Mais ce retour: Java.io.FileNotFoundException: Ce fichier ne peut pas être ouvert en tant que descripteur de fichier; c'est probablement compressé.
Au lieu de cette implémentation, j'ouvre directement le fichier en utilisant des fonctions de la forme ParcelFileDescriptor.
private void openRenderer(Context context,String fileName) throws IOException {
File file= FileUtils.fileFromAsset(context, fileName);
ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_WRITE);
mPdfRenderer = new PdfRenderer(parcelFileDescriptor);
}`
public class FileUtils {
private FileUtils() {
}
public static File fileFromAsset(Context context, String assetName) throws IOException {
File outFile = new File(context.getCacheDir(), assetName );
copy(context.getAssets().open(assetName), outFile);
return outFile;
}
public static void copy(InputStream inputStream, File output) throws IOException {
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(output);
boolean read = false;
byte[] bytes = new byte[1024];
int read1;
while((read1 = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read1);
}
} finally {
try {
if(inputStream != null) {
inputStream.close();
}
} finally {
if(outputStream != null) {
outputStream.close();
}
}
}
}
}
Ajoutez simplement:
aaptOptions {
noCompress "tflite"
}
À votre fichier build.gradle.
Si le fichier à obtenir dans le dossier des ressources dépasse 1 Mo, mon travail consiste à le compresser en tant que fichier Zip, à le décompresser avant de l'utiliser et à le stocker dans le stockage externe non compressé.
InputStream fileInputStream = getAssets().open("your_file.your_file_extension.Zip");
unzipInputStream(fileInputStream, "your_folder_in_external_storage");
La méthode unzipInputStream
que j'ai utilisée est la suivante:
public static void unzipInputStream(InputStream inputStream, String location)
{
try {
if ( !location.endsWith(File.separator) ) {
location += File.separator;
}
File f = new File(location);
if(!f.isDirectory()) {
f.mkdirs();
}
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(inputStream, BUFFER_SIZE));
try {
ZipEntry ze;
while ((ze = zin.getNextEntry()) != null) {
String path = location + ze.getName();
File unzipFile = new File(path);
if (ze.isDirectory()) {
if(!unzipFile.isDirectory()) {
unzipFile.mkdirs();
}
} else {
createParentDirectoriesIfMissing(unzipFile);
unzipFile(zin, unzipFile);
}
}
} finally {
zin.close();
}
} catch (Exception e) {
Log.e("", "Unzip exception", e);
}
}
private static void createParentDirectoriesIfMissing(File unzipFile)
{
File parentDir = unzipFile.getParentFile();
if ( null != parentDir ) {
if ( !parentDir.isDirectory() ) {
parentDir.mkdirs();
}
}
}
private static void unzipFile(ZipInputStream zin, File unzipFile) throws IOException
{
int size;
byte[] buffer = new byte[BUFFER_SIZE];
FileOutputStream out = new FileOutputStream(unzipFile, false);
BufferedOutputStream fout = new BufferedOutputStream(out, BUFFER_SIZE);
try {
while ( (size = zin.read(buffer, 0, BUFFER_SIZE)) != -1 ) {
fout.write(buffer, 0, size);
}
zin.closeEntry();
} finally {
fout.flush();
fout.close();
}
}