38 votes

Charger des fichiers plus gros que 1M à partir du dossier assets

Je deviens fou, j'ai créé un fichier objet, de sorte qu'il peut être lu avec un ObjectInputStream, et j'ai placé le dossier assets. La méthode fonctionne avec un fichier plus petit que 1M, et de donner d'erreur avec les fichiers de grande taille. J'ai lu que c'est une limite de la plate-forme Android, mais je sais aussi que peut être "facilement" à éviter. Ceux qui ont téléchargé le jeu Reging Tonnerre, par exemple, peut facilement voir que dans leurs actifs dossier est un fichier de 18,9 M de large. C'est mon code pour lire 1 objet à partir d'une ObjecInputStream

File f = File.createTempFile("mytempfile", "dat");
FileOutputStream fos = new FileOutputStream(f);

InputStream is = mc.getAssets().open(path,3);

ObjectInputStream ois=new ObjectInputStream(is);
byte[] data = (byte[]) ois.readObject();
fos.write(data);

fos.flush();
fos.close();
ois.close();
is.close();

maintenant, j'ai un fichier non compressé et je peux l'utiliser sans se soucier de l'erreur "Ce fichier ne peut être ouvert comme un descripteur de fichier; il est sans doute comprimé"

Cette fonction fonctionne bien avec des fichiers de taille inférieure à 1 M, avec des fichiers plus gros retour d'un java.io.IOException sur la ligne "ObjectInputStream ois=new ObjectInputStream(est);"

pourquoi??

49voto

Seva Alekseyev Points 31812

Face au même problème. J'ai découpé mon fichier de 4 Mo en morceaux de 1 Mo et, lors de la première exécution, j'ai joint les morceaux dans un dossier de données sur le téléphone. En prime, l'APK est correctement compressé. Les fichiers de morceaux s'appellent 1.db, 2.db, etc. Le code va comme ceci:

 File Path = Ctxt.getDir("Data", 0);
File DBFile = new File(Path, "database.db");

if(!DBFile.exists() || DatabaseNeedsUpgrade)  //Need to copy...
    CopyDatabase(Ctxt, DBFile);


static private void CopyDatabase(Context Ctxt, File DBFile) throws IOException
{
    AssetManager assets = Ctxt.getAssets();
    OutputStream outstream = new FileOutputStream(DBFile);
    DBFile.createNewFile();
    byte []b = new byte[1024];
    int i, r;
    String []assetfiles = assets.list("");
    Arrays.sort(assetfiles);
    for(i=1;i<10;i++) //I have definitely less than 10 files; you might have more
    {
        String partname = String.format("%d.db", i);
        if(Arrays.binarySearch(assetfiles, partname) < 0) //No such file in assets - time to quit the loop
            break;
        InputStream instream = assets.open(partname);
        while((r = instream.read(b)) != -1)
            outstream.write(b, 0, r);
        instream.close();
    }
    outstream.close();
}
 

33voto

fadden Points 17450

La limitation est comprimé actif. Si l'actif est non compressé, le système peut carte mémoire les données du fichier et utiliser le Linux de pagination de mémoire virtuelle du système pour tirer ou jeter blocs de 4 ko appropriées. (Le "zipalign" outil assure que non compressé actifs sont les mots alignés dans le fichier, ce qui signifie qu'ils vont être alignés dans la mémoire lorsqu'elle est directement mappée.)

Si l'actif est compressé, le système a pour décompresser de la chose entière de la mémoire. Si vous avez un 20 MO d'actifs, cela signifie que 20 mo de mémoire physique est liée par votre application.

Idéalement, le système devrait utiliser une sorte de fenêtre de compression, de sorte que seules les parties doivent être présentes, mais qui nécessite un certain fanciness dans l'actif de l'API et un système de compression qui fonctionne bien avec un accès aléatoire. Maintenant APK == Zip avec "dégonfler" la compression, ce qui n'est pas pratique.

Vous pouvez garder vos actifs non compressé en leur donnant un suffixe d'un type de fichier qui n'est pas compressé (par exemple ".png" ou ".mp3"). Vous pouvez également être en mesure de les ajouter manuellement pendant le processus de construction avec "zip -0" au lieu de les avoir regroupés par aapt. Ce sera probablement augmenter la taille de votre fichier APK.

9voto

Oliver Points 86

Comme Seva l'a suggéré, vous pouvez diviser votre fichier en morceaux. J'ai utilisé cela pour diviser mon fichier de 4 Mo

 public static void main(String[] args) throws Exception {  
    String base = "tracks";  
    String ext = ".dat";  
    int split = 1024 * 1024;  
    byte[] buf = new byte[1024];  
    int chunkNo = 1;  
    File inFile = new File(base + ext);  
    FileInputStream fis = new FileInputStream(inFile);  
    while (true) {  
      FileOutputStream fos = new FileOutputStream(new File(base + chunkNo + ext));  
      for (int i = 0; i < split / buf.length; i++) {  
        int read = fis.read(buf);  
        fos.write(buf, 0, read);  
        if (read < buf.length) {  
          fis.close();  
          fos.close();  
          return;  
        }  
      }  
      fos.close();  
      chunkNo++;  
    }  
  }  
 

Si vous n'avez pas besoin de combiner à nouveau les fichiers en un seul fichier sur le périphérique, utilisez simplement ce InputStream, qui les combine en un à la volée.

 import java.io.IOException;  
import java.io.InputStream;  

import android.content.res.AssetManager;  

public class SplitFileInputStream extends InputStream {  

  private String baseName;  
  private String ext;  
  private AssetManager am;  
  private int numberOfChunks;  
  private int currentChunk = 1;  
  private InputStream currentIs = null;  

  public SplitFileInputStream(String baseName, String ext, int numberOfChunks, AssetManager am) throws IOException {  
    this.baseName = baseName;  
    this.am = am;  
    this.numberOfChunks = numberOfChunks;  
    this.ext = ext;  
    currentIs = am.open(baseName + currentChunk + ext, AssetManager.ACCESS_STREAMING);  
  }  

  @Override  
  public int read() throws IOException {  
    int read = currentIs.read();  
    if (read == -1 && currentChunk < numberOfChunks) {  
      currentIs.close();  
      currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      return read();  
    }  
    return read;  
  }  

  @Override  
  public int available() throws IOException {  
    return currentIs.available();  
  }  

  @Override  
  public void close() throws IOException {  
    currentIs.close();  
  }  

  @Override  
  public void mark(int readlimit) {  
    throw new UnsupportedOperationException();  
  }  

  @Override  
  public boolean markSupported() {  
    return false;  
  }  

  @Override  
  public int read(byte[] b, int offset, int length) throws IOException {  
    int read = currentIs.read(b, offset, length);  
    if (read < length && currentChunk < numberOfChunks) {  
      currentIs.close();  
      currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      read += read(b, offset + read, length - read);  
    }  
    return read;  
  }  

  @Override  
  public int read(byte[] b) throws IOException {  
    return read(b, 0, b.length);  
  }  

  @Override  
  public synchronized void reset() throws IOException {  
    if (currentChunk == 1) {  
      currentIs.reset();  
    } else {  
      currentIs.close();  
      currentIs = am.open(baseName + currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      currentChunk = 1;  
    }  
  }  

  @Override  
  public long skip(long n) throws IOException {  
    long skipped = currentIs.skip(n);  
    if (skipped < n && currentChunk < numberOfChunks) {  
      currentIs.close();  
      currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      skipped += skip(n - skipped);  
    }  
    return skipped;  
  }  
}
 

Usage:
ObjectInputStream ois = new ObjectInputStream(new SplitFileInputStream("mytempfile", ".dat", 4, getAssets()));

7voto

yassine Points 89

une méthode pas propre consiste en une extension du fichier ttf en mp3

2voto

Ottavio Campana Points 1862

J'ai trouvé une autre solution, peut-être que ça vous intéresse.

à la racine de vos sources, où vous avez le build.xml fichier, vous pouvez remplacer le -package-resources cible dans le custom_rules.xml fichier, qui est utilisé pour ajouter / modifier cibles dans ant sans rien casser dans le système de construction d'applications Android standard.

Il suffit de créer un fichier comme celui-ci

 <?xml version="1.0" encoding="UTF-8"?>
<project name="yourAppHere" default="help">

    <target name="-package-resources" depends="-crunch">
        <!-- only package resources if *not* a library project -->
        <do-only-if-not-library elseText="Library project: do not package resources..." >
            <aapt executable="${aapt}"
                    command="package"
                    versioncode="${version.code}"
                    versionname="${version.name}"
                    debug="${build.is.packaging.debug}"
                    manifest="${out.manifest.abs.file}"
                    assets="${asset.absolute.dir}"
                    androidjar="${project.target.android.jar}"
                    apkfolder="${out.absolute.dir}"
                    nocrunch="${build.packaging.nocrunch}"
                    resourcefilename="${resource.package.file.name}"
                    resourcefilter="${aapt.resource.filter}"
                    libraryResFolderPathRefid="project.library.res.folder.path"
                    libraryPackagesRefid="project.library.packages"
                    libraryRFileRefid="project.library.bin.r.file.path"
                    previousBuildType="${build.last.target}"
                    buildType="${build.target}"
                    ignoreAssets="${aapt.ignore.assets}">
                <res path="${out.res.absolute.dir}" />
                <res path="${resource.absolute.dir}" />
                <nocompress /> <!-- forces no compression on any files in assets or res/raw -->
                <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
            </aapt>
        </do-only-if-not-library>
    </target>
</project>
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X