38

我快疯了,我创建了一个文件对象,所以它可以用 ObjectInputStream 读取,我放置了 assets 文件夹。该方法适用于小于 1M 的文件,但较大的文件会出错。我读到这是 Android 平台的限制,但我也知道可以“轻松”避免。例如,那些下载了雷霆雷霆游戏的人可以很容易地看到他们的资产文件夹中有一个 18.9M 大的文件。这是我从 ObjecInputStream 中读取 1 个对象的代码

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

现在我有一个未压缩的文件,我可以使用它而不必担心“此文件无法作为文件描述符打开;它可能已压缩”错误

此函数适用于小于 1M 的文件,较大的文件在“ObjectInputStream ois=new ObjectInputStream(is);”行返回 java.io.IOException

为什么??

4

11 回答 11

49

面临同样的问题。我已经将我的 4MB 文件切成 1MB 的块,并在第一次运行时将这些块加入到手机上的数据文件夹中。作为额外的奖励,APK 被正确压缩。块文件称为 1.db、2.db 等。代码如下所示:

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();
}
于 2010-06-22T14:14:17.777 回答
33

限制是压缩资产。如果资产未压缩,系统可以对文件数据进行内存映射,并使用 Linux 虚拟内存分页系统酌情拉入或丢弃 4K 块。(“zipalign”工具确保未压缩的资产在文件中是字对齐的,这意味着它们在直接映射时也会在内存中对齐。)

如果资产被压缩,系统必须将整个事物解压缩到内存中。如果您有 20MB 的资产,这意味着您的应用程序占用了 20MB 的物理内存。

理想情况下,系统会采用某种窗口压缩,因此只需要存在部分,但这需要资产 API 中的一些花哨的东西和一种适用于随机访问的压缩方案。现在 APK == 带有“deflate”压缩的 Zip,所以这不切实际。

您可以通过为资产提供未压缩文件类型的后缀(例如“.png”或“.mp3”)来保持资产未压缩。您也可以在构建过程中使用“zip -0”手动添加它们,而不是通过 aapt 捆绑它们。这可能会增加 APK 的大小。

于 2010-05-19T22:50:43.933 回答
9

就像 Seva 建议的那样,您可以将文件分成几块。我用它来分割我的 4MB 文件

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

如果您不需要再次在设备上将文件组合成一个文件,只需使用此 InputStream,它会即时将它们组合成一个文件。

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

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

于 2010-12-01T18:18:21.410 回答
7

将文件扩展名更改为 .mp3

于 2010-09-01T07:39:06.870 回答
2

我知道这是一个老问题,但我想到了一个很好的解决方案。为什么不将已经预压缩的文件存储在资产文件夹中。然后由于它已经是一个 zip 文件并因此被压缩,因此不需要再次压缩它。因此,如果您希望压缩文件以减小 apk 的大小,但又不想处理拆分文件,我认为这更容易。

当您需要从设备上读取该文件时,只需将输入流包装在一个 zipinputstream http://developer.android.com/reference/java/util/zip/ZipInputStream.html

于 2012-02-24T01:05:49.593 回答
2

我找到了另一个解决方案,也许你对此感兴趣。

在您拥有该build.xml文件的源的根目录中,您可以覆盖文件中的-package-resources目标,该目标custom_rules.xml用于在 ant 中添加/修改目标,而不会破坏标准 android 应用程序构建系统中的任何内容。

只需创建一个包含以下内容的文件:

<?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>
于 2013-04-02T15:41:59.513 回答
0

我将大文件放在原始文件夹中,而不是资产文件夹。这个对我有用。

于 2010-05-19T11:59:42.257 回答
0

添加文件扩展名是 mp3。我使用 mydb.mp3in assets 文件夹并复制 .this 运行没有错误。显示检查它。

于 2013-01-15T10:32:55.353 回答
0

使用“Win Hex”等程序将数据库设置为多个部分,您可以从链接下载

并继续从 assets 文件夹加载大于 1M 的文件

于 2015-05-01T12:09:43.120 回答
0

使用GZIP将是另一种方法。您只需要将InputStream包装在GZIPInputStream中。

我将它用于大小约为 3.0 MB 的数据库,输出压缩文件约为 600KB。

并复制它:

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();
    }
于 2013-10-06T09:05:24.100 回答
-1

我使用 NetBeans 构建包,但我没有找到如何更改 AAPT 的设置。我没有尝试过png,但mp3是压缩的。我可以编译包,然后用参数-0进入assets文件夹吗?正确的命令是什么?

于 2010-05-20T09:32:06.903 回答