2010-05-18 25 views
36

Me estoy volviendo loco, creé un objeto de archivo, por lo que se puede leer con ObjectInputStream y coloqué la carpeta de activos. El método funciona con un archivo más pequeño que 1M y da error con archivos más grandes. He leído que es un límite de la plataforma Android, pero también sé que se puede evitar "fácilmente". Aquellos que han descargado el juego Reging Thunder, por ejemplo, pueden ver fácilmente que en su carpeta de activos hay un archivo de 18.9M de ancho. Este es mi código de lectura 1 objeto de una ObjecInputStreamCargar archivos mayores a 1M desde la carpeta de activos

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(); 

ahora tengo un archivo sin comprimir y puedo usarlo sin tener que preocuparse por el error "Este archivo no se puede abrir como un descriptor de archivo, es probable comprimido "

Esta función funciona bien con archivos menores a 1M, con archivos más grandes devuelve java.io.IOException en la línea" ObjectInputStream ois = new ObjectInputStream (is); "

¿por qué?

Respuesta

48

Ha tenido el mismo problema. Corté mi archivo de 4MB en trozos de 1 MB, y en la primera ejecución uní los trozos en una carpeta de datos en el teléfono. Como una ventaja adicional, el APK está comprimido correctamente. Los archivos se llaman trozo 1.db, 2.db, etc. El código es así:

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(); 
} 
+1

gracias por su respuesta. Pero, ¿qué tipo de herramienta usa para cortar su db grande en cada fragmento más pequeño? – anticafe

+0

Intenté algunos programas gratuitos listos, luego escribió otro. :) Es una pieza de 20 líneas en C. C. –

+6

en Linux y Mac. Hay una herramienta de línea de comandos llamada división. – mclin

0

En lugar de la carpeta assets, he puesto mis archivos grandes en la carpeta raw. Esto funciona para mi.

+0

Siempre tengo el mismo problema: "Los datos exceden UNCOMPRESS_DATA_MA X (1314625 vs 1048576) " gracias de todos modos – Syco

+0

Estoy usando Eclipse y mi aplicación Navkar Matra http://www.androlib.com/android.application.com-app-navkarmantra-Cxin.aspx funciona maravillosamente con un archivo de 2 MB. – the100rabh

32

La limitación está en los activos comprimidos. Si el activo no está comprimido, el sistema puede mapear la memoria de los datos del archivo y usar el sistema de paginación de la memoria virtual de Linux para extraer o descartar fragmentos de 4K, según corresponda. (La herramienta "zipalign" asegura que los activos no comprimidos se alineen con la palabra en el archivo, lo que significa que también se alinearán en la memoria cuando se mapeen directamente).

Si el activo está comprimido, el sistema tiene que descomprimir todo cosa a la memoria. Si tienes un activo de 20MB, eso significa que tu aplicación está atada a 20MB de memoria física.

Lo ideal es que el sistema emplee algún tipo de compresión en ventanas, por lo que solo deben estar presentes las partes, pero eso requiere cierta sofisticación en la API de activos y un esquema de compresión que funciona bien con acceso aleatorio. En este momento, APK == Zip con compresión "desinflar", por lo que no es práctico.

Puede mantener sus activos sin comprimir dándoles un sufijo de un tipo de archivo que no se comprime (por ejemplo, ".png" o ".mp3"). También puede agregarlos manualmente durante el proceso de compilación con "zip -0" en lugar de tenerlos empaquetados por aapt. Esto probablemente aumentará el tamaño de su APK.

+0

Acabo de enfrentar este problema hoy y podría hacerlo funcionar cambiando el sufijo del nombre de archivo a .mp3 como se sugiere. .png no funcionó sin embargo. AssetManager dice que no puede encontrar el archivo :(¡Muchas gracias por el sufijo! –

+2

este [post] (http://stackoverflow.com/questions/3177026/problem-while-opening-asset-file-with- the-help-of-content-provider) tiene una lista de tales extensiones. – Samuel

-1

Uso NetBeans para compilar el paquete y no encontré cómo cambiar la configuración de AAPT. No he probado el png, pero los mp3 están comprimidos. Puedo compilar el paquete y luego ingresar a la carpeta de activos con el parámetro -0? ¿cuál sería el comando correcto para usar?

7

une méthode pas propre Consiste un cambiador de extensión du fichier ttf un mp3

+14

traducción: cambie la extensión de archivo a .mp3 – eggie5

+0

buen trabajo yassine, pero con esta solución, no hay ningún problema en playStore (cuando subo el apk en playStore) –

8

Al igual que Seva sugerido puede divide tu archivo en fragmentos.He utilizado este para dividir mi archivo de 4 MB

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 no es necesario combinar los archivos en un único archivo en el dispositivo de nuevo, sólo tiene que utilizar este InputStream, que los combina en un solo sobre la marcha.

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; 
    } 
} 

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

+0

Su (s) método (s) parece (n) trabajo, pero en la práctica tuve dos problemas. En el primer método (main - Java - splitter method) me enfrento al problema cuando el archivo tenía exactamente 1024 * n (n> 0) de largo. Y después de leer los últimos bytes 'int read' tiene el valor' -1' y 'fos.write' fallado. Cambié el mecanismo de escritura como 'if (read> -1) {fos.write (buf, 0, read); } '. El siguiente problema pertenece a la clase 'SplitFileInputStream'. El método 'public int read (byte [] b)' no funciona bien. Así que lo cambié analógicamente como el método 'public int read (byte [] b, int offset, int length)'. – zmeda

+0

Parece algo así como 'public int read (byte [] b) throws IOException { \t \t int read = currentIs.read (b); \t \t si (leer zmeda

2

Sé que esto es una vieja pregunta, pero pensé en una buena solución. ¿Por qué no almacena el archivo ya precomprimido en la carpeta de activos? Entonces, como ya es un archivo zip y, por lo tanto, comprimido, no será necesario volver a comprimirlo. Entonces, si quisieras que el archivo se comprimiera para disminuir el tamaño de tu apk, pero no quieres lidiar con la división de archivos, creo que es más fácil.

Cuando se necesita leer ese archivo apagar el dispositivo simplemente envolver el flujo de entrada en una carpeta es ZipInputStream uso mp3.I activos mydb.mp3in http://developer.android.com/reference/java/util/zip/ZipInputStream.html

+0

Aquí está el ejemplo del código de cómo abrir el archivo zip en lugar del archivo gz https://github.com/jgilfelt/android-sqlite-asset-helper/blob/master/library/src/main/java/com/ readystatesoftware/sqliteasset/SQLiteAssetHelper.java # L449 Esto parece funcionar con archivos de más de 1M. –

0

extensión de archivo complemento y copiar .Esta correr sin verificación error.show eso.

+0

gracias compañero esto es un truco muy simple que funciona – Jack

2

Encontré otra solución, tal vez le interese.

En la raíz de sus fuentes, donde se tiene el archivo build.xml, puede sobrescribir el objetivo -package-resources en el archivo custom_rules.xml, que se utiliza para añadir/modificar los objetivos de la hormiga sin romper nada en el sistema de aplicación de construcción estándar de Android.

Basta con crear un archivo con este contenido:

<?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> 
0

Usando GZIP sería otro método. solo necesita envolver InputStream dentro de GZIPInputStream.

Lo usé para una base de datos cuyo tamaño de aproximadamente 3.0 MB y el archivo de compresión de salida era de aproximadamente 600KB.

  • Para copiar DB en el plazo abetos, que gzipped mi archivo .db fuente utilizando GZIP tool.
  • Luego se renombró a .jpg para evitar más compresión (estos procesos se realizan antes de compilar el ARCHIVO APK).
  • Luego de leer el archivo GZIP comprimido desde assetss

y copiarlo: base de datos de

private void copydatabase() throws IOException { 
     // Open your local db as the input stream 
     InputStream myinput = mContext.getAssets().open(DB_NAME_ASSET); 
     BufferedInputStream buffStream = new BufferedInputStream(myinput); 
     GZIPInputStream zis = new GZIPInputStream(buffStream); 

     // Path to the just created empty db 
     String outfilename = DB_PATH + DB_NAME; 

     // Open the empty db as the output stream 
     OutputStream myoutput = new FileOutputStream(outfilename); 


     // transfer byte to inputfile to outputfile 
     byte[] buffer = new byte[1024]; 
     int length; 
     while ((length = zis.read(buffer)) > 0) { 
      myoutput.write(buffer, 0, length); 
     } 

     // Close the streams 
     myoutput.flush(); 
     myoutput.close(); 
     zis.close(); 
     buffStream.close(); 
     myinput.close(); 
    } 
Cuestiones relacionadas