Ich werde verrückt, ich habe ein Dateiobjekt erstellt, damit es mit ObjectInputStream gelesen werden kann, und ich habe den Ordner mit den Assets platziert .. Die Methode arbeitet mit einer Datei, die kleiner als 1 MB ist, und gibt bei größeren Dateien einen Fehler aus. Ich habe gelesen, dass dies eine Beschränkung der Android-Plattform ist, aber ich weiß auch, dass "leicht" vermieden werden kann . Wer beispielsweise das Spiel "Reging Thunder" heruntergeladen hat, kann leicht erkennen, dass sich in seinem Assets-Ordner eine Datei mit 18,9 MB befindet large . Dies ist mein Code zum Lesen eines Objekts aus einem 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();
ich habe jetzt eine unkomprimierte Datei und kann sie verwenden, ohne sich um den Fehler kümmern zu müssen. "Diese Datei kann nicht als Dateideskriptor geöffnet werden. Sie ist wahrscheinlich komprimiert."
Diese Funktion eignet sich gut für Dateien, die kleiner als 1 MB sind. Größere Dateien geben in der Zeile eine Java.io.IOException zurück. "ObjectInputStream ois = new ObjectInputStream (is);"
warum??
Konfrontiert mit demselben Problem. Ich habe meine 4-MB-Datei in 1-MB-Blöcke aufgeteilt, und beim ersten Lauf füge ich die Blöcke in einem Datenordner auf dem Telefon zusammen. Als zusätzlicher Bonus wird die APK richtig komprimiert. Die Chunk-Dateien heißen 1.db, 2.db usw. Der Code lautet folgendermaßen:
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();
}
Die Einschränkung gilt für komprimierte Assets. Wenn das Asset unkomprimiert ist, kann das System die Dateidaten im Speicher zuordnen und das virtuelle Paging-System für virtuellen Arbeitsspeicher verwenden, um 4K-Blöcke nach Bedarf einzuziehen oder zu verwerfen. (Das Tool "zipalign" stellt sicher, dass nicht komprimierte Assets in der Datei Word-ausgerichtet sind. Dies bedeutet, dass sie auch direkt im Speicher ausgerichtet werden, wenn sie direkt zugeordnet werden.)
Wenn das Asset komprimiert ist, muss das System die gesamte Sache in den Speicher dekomprimieren. Wenn Sie über ein 20-MB-Asset verfügen, bedeutet dies, dass 20 MB physischer Speicher von Ihrer Anwendung gebunden sind.
Im Idealfall würde das System eine Art Fensterkomprimierung verwenden, so dass nur Teile vorhanden sein müssen. Dies erfordert jedoch etwas Fantasie in der Asset-API und ein Komprimierungsschema, das bei Direktzugriff gut funktioniert. Im Moment ist APK == Zip mit "deflate" Komprimierung, das ist also nicht praktikabel.
Sie können Ihre Assets unkomprimiert halten, indem Sie ihnen ein Suffix eines Dateityps geben, der nicht komprimiert wird (z. B. ".png" oder ".mp3"). Sie können sie möglicherweise auch während des Erstellungsprozesses manuell mit "Zip -0" hinzufügen, anstatt sie von aapt gebündelt zu haben. Dies erhöht wahrscheinlich die Größe Ihres APK.
Wie Seva vorgeschlagen hat, können Sie Ihre Datei in Stücke aufteilen. Ich habe das benutzt, um meine 4MB Datei aufzuteilen
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++;
}
}
Wenn Sie die Dateien auf dem Gerät nicht erneut zu einer einzigen Datei kombinieren müssen, verwenden Sie einfach diesen InputStream, der sie im Handumdrehen zu einer einzigen Datei kombiniert.
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;
}
}
Verwendungszweck:ObjectInputStream ois = new ObjectInputStream(new SplitFileInputStream("mytempfile", ".dat", 4, getAssets()));
Ändern Sie die Dateierweiterung in .mp3
Ich habe eine andere Lösung gefunden, vielleicht interessieren Sie sich dafür.
Im Stammverzeichnis Ihrer Quellen, wo Sie über die Datei build.xml
verfügen, können Sie das Ziel -package-resources
in der Datei custom_rules.xml
überschreiben, die zum Hinzufügen/Ändern von Zielen in ant verwendet wird, ohne dass das Standardsystem der Android-App-Erstellung beschädigt wird.
Erstellen Sie einfach eine Datei mit diesem Inhalt:
<?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>
Ich weiß, dass dies eine alte Frage ist, aber ich habe an eine gute Lösung gedacht. Warum speichern Sie die Datei nicht bereits im ZIP-Ordner im Asset-Ordner .. Dann wird sie nicht komprimiert, da es sich bereits um eine ZIP-Datei handelt müssen erneut komprimiert werden. Wenn Sie möchten, dass die Datei komprimiert wird, um die Größe Ihrer Apk zu verringern, Sie sich jedoch nicht mit dem Aufteilen von Dateien beschäftigen möchten, ist dies meiner Meinung nach einfacher.
Wenn Sie diese Datei vom Gerät lesen müssen, wickeln Sie den Eingabestrom einfach in einen zipinputstream ein http://developer.Android.com/reference/Java/util/Zip/ZipInputStream.html
setzen Sie die Datenbank mit einem Programm auf mehrere Teile, z. B. "Win Hex". Sie können sie von Link herunterladen.
und fortsetzen Laden Sie Dateien, die größer als 1 MB sind, aus dem Ordner Assets
dateierweiterung hinzufügen ist mp3.Ich benutze mydb.mp3in Assets Ordner und kopiere .dies läuft ohne Fehler.
Anstelle des Assets-Ordners habe ich meine großen Dateien im Raw-Ordner abgelegt. Für mich geht das.
Die Verwendung von GZIP wäre eine andere Methode. Sie müssen nur InputStream in GZIPInputStream einschließen.
Ich habe dies für eine Datenbank mit einer Größe von etwa 3,0 MB und einer Ausgabekomprimierungsdatei von etwa 600 KB verwendet.
und kopiere es:
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();
}