android: parse big portion of data - android

I'm playing with android and trying to receive file (pdf book). To do this I wrote Servelt that encode the book into base64 string and put it in the XML document. That document contains name, author and IBSN fields also. I successfully receve it. In case of a small book I can even decode it and open. But if the size is more than 2mb I get OutOfMeoryError.
My parser code is:
public void characters(char[] ch, int start, int length)
throws SAXException {
if(builder==null) builder = new StringBuilder();
builder.append(new String(ch, start, length));
Then I do the following:
fos = new FileOutputStream(Environment.getExternalStorageDirectory().toString()+"/book.pdf", false);
byte[] toWrite = Base64.decode(builder.toString(), Base64.DEFAULT);
fos.write(toWrite);
fos.flush();
fos.close();
Does anybody know how can I parse it wothout error? I've tried to parse it in
characters method (I mean using small buffer) but it fails... Illegal base 64 string.

Does anybody know how can I parse it wothout error?
You can't, most likely, as there is not enough RAM for you to work with.
Sensible programmers would not convert a large binary file into "base64 string and put it in the XML document". Sensible programmers would:
put the metadata (name, author, ISBN) in property fields within the PDF, which is a fine solution for Java, but may be a problem for Android, as the libraries to get at those fields may or may not work (e.g., iText); or
download the two files separately (one with the XML of metadata, which contains the URL to the PDF file); or
package the PDF and the XML file into a single archive (e.g., ZIP) and download the archive
There may be other solutions than those three, but they should give you a starting point.

Related

class MemoryFile of any use?

I planned to use shared memory between an activity and a service in separate process to transfer big content between them.
To that end I read every info I have found on MemoryFile and how to transfer it between activity and specifically this stackoverflow entry what is the use of MemoryFile in android .
But I am unable to call getParcelFileDescriptor (using the described solution) on my android version 4.xx. It seems that the method does not exist anymore.
Nevertheless I come to the following code to send a ParcelFileDescriptor to my service
(take it as pseudo code, but in fact it is ruboto code):
shm = MemoryFile.new("picture", 1000)
f = shm.getFileDescriptor()
p = ParcelFileDescriptor.dup( f)
b = Bundle.new()
b.putParcelable( "shm", p)
msg.setData( b)
service.send( msg)
To test that the shared memory is properly accessible, I have written a string in it,
and try to retrieve it on the service side.
I have the following (true java) code to do that:
Parcelable p = msg.getData().getParcelable("shm");
ParcelFileDescriptor shm = (ParcelFileDescriptor) p;
FileDescriptor f = shm.getFileDescriptor();
if( f.valid()) {
FileInputStream in = new FileInputStream( f);
String s = readString( in); // this fail!
}
Every thing is ok, f is valid but I cannot read from the received fileDiscriptor, I get:
java.io.IOException: read failed: EINVAL (Invalid argument)
The code for the reading is the following:
public String readString(InputStream inputStream) throws IOException {
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String s = r.readLine();
return s;
}
So two question:
I am doing wrong ? (in any of the side)
or does the MemoryFile amputed from #getParcelFileDescriptor is now
unusable as a mean to share memory betweens two process ?
In this latter case, I fail to see any interest in this class then...
I have seen other article mentioning JNI code to used shared memory but would like to avoid that additional complexity.
I managed to transfer data between applications through a MemoryFile on Android 4.0.4 using shm.getFileDescriptor() and ParcelFileDescriptor.dup(f) so, fortunately, this class is still usable. In your case the problem may be in the content of the file, although I don't know how it can cause an Invalid argument error. Try writing and reading a fixed-length byte array instead of a string (which you don't actually write in the provided code) and read it simply with InputStream.read(buffer).

How do I go about setting TextView to display UTF-8 when the String is not an embedded Resource?

I'm encountering an odd situation whereby strings that I load from my resource XML file that have Spanish characters in them display correctly in my TextViews, but strings that I'm fetching from a JSON file that I load via HTTP at runtime display the missing char [] boxes
ESPAÑOL for example, when embedded in my XML strings works fine, but when pulled from my JSON is rendered as SPAÃ[]OL, so the Ñ is transformed into a à and a missing char!
I'm not sure at what point I need to intercept these strings and set the correct encoding on them. The JSON text file itself is generated on the server via Node, so, I'm not entirely sure if that's the point at which I should be encoding it, or if I should be encoding the fileReader on the Android side, or perhaps setting the TextView itself to be of some special encoding type (I'm unaware that this is an option, just sort of throwing my hands in the air, really).
[EDIT]
As per ianhanniballake's suggestion I am logging and seeing that the screwy characters are actually showing up in the log as well. However, when I look at the JSON file with a text viewer on the Android file system (it's sitting on the SDCARD) it appears correct.
So, it turned out that the text file was, indeed, encoded correctly and the issue was that I wasn't setting UTF-8 as my encoding on the FileInputStream...
The solution is to read the file thusly:
static String readInput() {
StringBuffer buffer = new StringBuffer();
try {
FileInputStream fis = new FileInputStream("myfile.json");
InputStreamReader isr = new InputStreamReader(fis, "UTF8");
Reader in = new BufferedReader(isr);
int ch;
while ((ch = in.read()) > -1) {
buffer.append((char) ch);
}
in.close();
return buffer.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

Android can't open file using LibGDX

I'm currently writing a LibGDX game and I need to open an XML file for a spritesheet. Unfortunately, when I try to open the file, I get an IOException. The files exist in the right places, the project's cleaned and up to date, etc.
Here's the kicker: LibGDX will open and display image files in the same directory, and in fact will obtain the XML file in its own FileHandle object (I've had it logcat the size and it matches up). Yet when I send the file off to a SAX parser, I get an exception leading me back to the file saying that it can't open it.
Here's the problem line of code inside the ClipSprite class:
private void parseConfigFile(FileHandle file) throws ParserConfigurationException, SAXException
{
//Use a SAX parser to get the data out of the XML file.
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLConfigHandler handler = new XMLConfigHandler();
//Parse through the document
try
{
parser.parse(file.path(),handler); //IOException here
}
And here is the code where I select the file in the game in the create method in the main LibGDX class
//This loads a png file
texture = new Texture(Gdx.files.internal("data/graphics/CDE1/CDE1.png"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
//Here I load the xml file and it tells me how many bytes it is
Gdx.app.error("File","xml bytes:"+Gdx.files.internal("data/graphics/CDE1/CDE1.xml").length());
//Here I load the same file and it crashes
//Note that it looks in the subdirectory and finds /CDE1.xml properly
animation = new ClipSprite("data/graphics/CDE1");
Error from logcat:
java.io.IOException: Couldn't open data/graphics/CDE1/CDE1.xml
Why can it identify the file, yet I still get IOExceptions about being unable to open it? Thanks for the help, and I'll provide any more information if necessary.
Just to coalesce all the comments above into a single answer in case someone else runs into this or something similar:
Try Gdx.files.local() to load your file, instead of Gdx.files.internal(), as the "internal" files are read out of the (compressed) APK and may not be "visible" like a normal file. (This depends a lot on what the code you're passing a pathname to wants to do with it.)
For a libGDX FileHandle file object use file.file() to pass a File handle, instead of file.path() to pass a String that might get misinterpreted.
For a libGDX FileHandle file use file.read() to pass an InputStream directly to the consumer (instead of passing a string or file handle and expecting them to open it).
A libGDX "internal" file maps to an Android AssetManager file, so they can be a bit wonky in some cases. Specifically, they may be compressed and stored within a .jar or .apk and not visible to traditional file reading functions.

How to access image folder in jni function

I have a android project and within that project I have a folder that contain some images.Now what i want is to access that folder within a jni function. So how should I do that?
An Android application is essentially a zip file, but there are a lot of nice helpers to get to those resources. First thing you need to remember is that these files are not actually in a folder, unless you put them there.
That said, there are a number of possible solutions for you situation.
JNI (almost) all the way
The most JNI-y way to go about things is to use have a method like
private native void processResources(AssetManager assets);
in your Activity, and use JNI to pry all the necessary resources from it, put them somewhere on disk, and process them.
Mix in a bit of Java
Probably an easier way is to have some Java code that helps with the resource management; something like
private void processAll() {
for (String resource : getResources().getAssets().list(mydir)) {
process(getResources().getAssets().open(resource));
}
// exception handling omitted for readability
}
private void process(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (int read = in.read(buf); read > 0; read = in.read(buf)) {
out.write(buf, 0, len);
}
process(out.toByteArray());
}
private native void process(byte[] resource);
and do the native processing based on the byte[].
Prepare files on the file system
A third solution is to move the files out of your bundle and onto the file system, and then point your native code to that directory.
So?
In the end, it all depends on what you exactly want to do with your resources, and whether or not they really need to be on the file system. I would keep things as close to Java as possible, and only go native when you really have to (e.g., your image manipulation code).

Problem with audio

In my android app I can record audio and save it on the phone/sdk. I checked that it is audible and clear when i play it back on the phone. The size of the audio file it created is 5.9kb(.amr format).
Next i upload the file to the server, it stores the audio on sql db. The upload is successful. When the uploaded audio is played, it is all garbled...
In the database i store the audio in a column with datatype image and is of length 16.
My question is ..why is the noise garbled after upload. How do i verify that the audio is saved correctly without any noise added.
Code for file upload
InputStream = new DataInputStream(new FileInputStream( FileName));
byte[] responseData = new byte[10000];
int length = 0;
StringBuffer rawResponse = new StringBuffer();
while (-1 != (length = InputStream.read(responseData)))
rawResponse.append(new String(responseData, 0, length));
String finalstring = rawResponse.toString();
voicedataArray = finalstring.getBytes();
Your problem is very much likely due to the use of StringBuffer to buffer the response. A character in Java is a two-byte entity corresponding to a Unicode character point. The documentation for String#getBytes() says:
Returns a new byte array containing the characters of this string
encoded using the system's default charset.
So there's no guarantee that the bytes you are passing in, being converted to characters, then back to bytes is the same stream you passed in the first place.
I think you would need to code your solution using a dynamically expanding byte buffer in place of the StringBuffer.
Also, two notes about the usage of StringBuffer:
1) All accesses to the StringBuffer are synchronized, so you're paying a performance penalty. StringBuilder is a modern-day replacement that doesn't do synchronization under the hood.
2) Each time you append to the StringBuffer:
rawResponse.append(new String(responseData, 0, length));
you are allocating a new string and throwing it away. That's really abusive to the garbage collector. StringBuffer actually has a form of append() that will directly take a char array, so there is no need to use an intermediate String. (But you probably don't want to use a StringBuffer in the first place).

Categories

Resources