从资产文件夹加载大于1M的文件

我疯了,我创build了一个文件对象,所以它可以用ObjectInputStream读取,我放置的资产文件夹。 该方法使用小于1M的文件,并给较大的文件提供错误。 我读到的是Android平台的限制,但我也知道可以“轻松”避免。 那些已经下载了Reging Thunder游戏的例子,可以很容易的看到,在他们的资产文件夹中是一个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.IOExceptionexception。

为什么??

面临同样的问题。 我已经把我的4MB文件分割成了1MB大小的块,在第一次运行的时候,我把块join到手机的数据文件夹中。 作为一个额外的好处,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(); } 

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

如果资产被压缩,则系统必须将整个事物解压缩到内存。 如果你有一个20MB的资源,这意味着20MB的物理内存被你的应用程序捆绑在一起。

理想情况下,系统会采用某种窗口化的压缩方式,这样只有部分需要存在,但是这需要资产API中的一些幻想,以及适合随机访问的压缩scheme。 现在APK == Zip与“deflate”压缩,所以这是不实际的。

你可以通过给它们一个不压缩的文件types(例如“.png”或“.mp3”)的后缀来保持你的资源不被压缩。 您也可以在构build过程中使用“zip -0”手动添加它们,而不是将它们捆绑在aapt中。 这可能会增加APK的大小。

像塞瓦build议你可以分割你的文件。 我用这个来分割我的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()));

mp3格式转换器

我find了另一个解决scheme,也许你对它感兴趣。

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

只需用这个内容创build一个文件:

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

我知道这是一个古老的问题,但我想到了一个好的解决scheme。 为什么不将文件预先存储在资产文件夹中? 然后,因为它已经是一个zip文件,因此压缩它不需要再次压缩。 所以,如果你想压缩文件来减小你的apk的大小,但是你不想处理分割文件,我认为这很容易。

当你需要从设备上读取这个文件时,只需要将inputstream包装在一个zipinputstream中。http: //developer.android.com/reference/java/util/zip/ZipInputStream.html

添加文件扩展名是mp3.I使用mydb.mp3in资源文件夹并复制。这个运行没有error.show检查它。

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

我用这个数据库大小约为3.0 MB,输出压缩文件大约为600KB。

  • 对于第一次运行的数据库,我使用GZIP工具来压缩源代码.db文件。
  • 然后将其重命名为.jpg以避免更多压缩(这些过程在编译APK文件之前完成)。
  • 然后从asset中读取压缩的GZIP文件

并复制它:

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

用程序例如“Win Hex”将数据库设置为多个部分,可以从Link下载

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

而不是资产文件夹,我把我的大文件放在原始文件夹中。 它适用于我。

我使用NetBeans来构build包,但是我没有find如何更改AAPT的设置。 我没有尝试的PNG,但MP3被压缩。 我可以编译这个包,然后用参数-0inputassets文件夹。 什么是正确的命令使用?