I sometimes get an OutOfMemoryError when posting a large file in Android. This is the code I'm using. Am I doing something wrong?
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setDoInput(true);
con.setDoOutput(true);
ostream = new DataOutputStream(con.getOutputStream());
byte[] buffer = new byte[1536];
int count;
while ((count = fileInput.read(buffer)) != -1) {
ostream.write(buffer, 0, count); // This sometimes causes OutOfMemoryError
}
ostream.flush();
Would calling ostream.flush() inside the while loop do any good?
If you do it like that, the whole POST must be buffered in memory. This is because it needs to send the Content-Length header first.
Instead, I think you want to use the Apache HTTP libraries, including FileEntity. That will let it figure out the length before reading the file. You can use this answer as a starting point. But the second parameter to the FileEntity constructor should be a mime type (like image/png, text/html, etc.).
HTTP connection is fine, but HTTPS will trigger Out of Memory error because there is a bug in HttpsURLConnectionImpl.java in Android 2.3.4 (verified on my tablet), and it's fixed in Android 4.1 (I have checked the source code).
By the way, he should avoid buffering the whole POST data in RAM by adding "con.setChunkedStreamingMode(0);" immediately after "con.setDoOutput(true);" statement.
Related
I'm using this code to parse a JSON array I'm getting from my server.
try {
URL u = new URL("http://54.68.139.250/get_user_likes");
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setRequestMethod("GET");
conn.connect();
InputStream is = conn.getInputStream();
byte[] b = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ( is.read(b) != -1)
baos.write(b);
String JSONResp = new String(baos.toByteArray());
JSONArray arr = new JSONArray(JSONResp);
for (int i=0; i < arr.length(); i++) {
result.add(convertArticle(arr.getJSONObject(i)));
}
return result;
}
catch(Throwable t) {
t.printStackTrace();
}
return null;
This code works great on my phone. Unfortunately, when I'm using a Genymotion emulator with the virtual device of Google Nexus 7, the JSON array is slightly altered. 95% of the JSON array is fine, but it is truncated near the very end and is randomly missing about 4 characters of the json array at character 1253 so I'm getting:
org.json.JSONException: Expected ':' after top_id at character 1253 of [{"top_id":6,"top_url":
I'm thinking this is some memory problem with the emulator. Its base memory is 1024. Increasing that amount though doesn't change anything.
Any tips as to the reason behind the problem would be greatly appreciated. Also, feel free to comment on my code if you see room for improvement. :)
That's weird.
I can think of two things to try:
Check the encoding of the server and the encoding of the String constructor.
It's possible that the server is decoding with, say, Latin-1 (ISO-8859-1) and the String is encoding with UTF-8. But JSON is supposed to be sent in Unicode and the default charset for Android is UTF-8. Check the HTTP Content-type response header; it should say: application/json; charset=utf-8. If the charset part isn't there, do some investigation and find out what character set your server is using for decoding to HTTP stream. And check that the default charset on the phone and the emulator is the same; it should be UTF-8.
Try calling flush() on the ByteArrayOutputStream after you read the data and before you construct the String.
It may be that there is a slight difference between the phone OS and the emulator OS on how stream data is transferred/buffered/flushed.
If flush() doesn't work, try rewriting the code without using ByteArrayOutputStream. You could, for example, wrap the input stream with an InputStreamReader, which reads characters, not bytes, then append the characters using a StringBuilder or StringBuffer.
One way you could make the code better is to use JSONReader instead of JSONArray or JSONObject. The JSONReader would wrap an InputStreamReader which in turn wraps the HTTP input stream. It can be faster and more memory efficient since you don't have to read the entire input stream before starting to parse the data. When the server is sending a LOT of JSON data, that can make a big difference.
You should check the return value of is.read(). Change
while ( is.read(b) != -1)
baos.write(b);
to
int nread;
while ( (nread=is.read(b)) != -1)
baos.write(b, 0, nread);
I am trying to download files from a server in android and show progress dialog using code very similar to the answer provided in this thread but i am not able to get content length in HttpURLConnection's getContentLength() method. Content length for all files is -1.
For the same file, i get correct content length in iOS app with NSHTTPURLResponse's expectedContentLength method.
Is there some basic difference in the way these methods fetch the content length for an http connection/response?
EDIT 1:
Tried following few things as suggested some answers and comments.
Set Accept-Encoding header to identity
Fetching the content length as string (from header field Content-Length) and then converting it to long
Tried conn.getContent().toString().length() instead of getContentLength()
None of these worked for me yet.
What baffles me most is i get the content length in iOS but not on android.
EDIT 2:
Heres my iOS and Android code for comparison -
iOS:
NSURLRequest *request = [NSURLRequest requestWithURL:self.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:1200.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
Android:
URL url = new URL(downloadUrlString);
connection = (HttpURLConnection) url.openConnection();
connection .setRequestProperty("Accept-Encoding", "identity");
connection.connect();
The only difference i can see is caching.
So i added following line in android code as well but nothing changed.
connection.setUseCaches(true);
try this:
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn .setRequestProperty("Accept-Encoding", "identity");
conn.connect();
if you just need to content length, can you try
conn.getContent().toString().length()
where conn is the HttpURLConnection object
Root cause for this problem turned out to be Cookies.
I am using a web view in one of the activities in my application. Some cookies are stored by the web view. All other REST api and file download requests work without those cookies however for a particular type of requests, the cookies are necessary.
Apparently, android web view and the connection requests do not share cookies out of the box like iOS. As a result i had to make changes in my code to make sure that the HttpUrlConnection uses WebKit's cookie store. I did it using method described in the accepted answer for this question.
I am getting a java.io.IOException: Value too large for defined data type when uploading a large file almost 2.1GB to a server. and My code is :
URL oUrl = new URL(servierUrl);
HttpURLConnection connection = (HttpURLConnection) oUrl.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form- data");
connection.setRequestProperty("Content-Length", String.valueOf(nChunkSize));
connection.setConnectTimeout(1800000);
InputStream oFileInputStream = new FileInputStream(new File(sFilePath));
OutputStream outputStream = new DataOutputStream(connection.getOutputStream());
.
.
. // here I'm dividing the stream chunks, every chunk 5Mb and uploading the chuck to server
but in the chunk #410 (5Mb * 410) it cause an exception return the exception as I mentioned above.
before that every chunk I upload it success
.
.
So any help please.
My guess- something is using an int to hold the length of the file. This is overflowing at 2^31-1, or about 2 billion bytes. Looks like an issue in either the http library or the output stream. You're going to need to either break up that file or replace the breaking component by one that works. Which component is breaking can be found by looking higher up in the stack trace and seeing where the crash comes from.
I've read in numerous places that Android does not have native gif support. As a result, my code is failing on BitmapFactory.decodeByteArray. Here's my code:
URL url = new URL(imgURL); //you can write here any link
HttpURLConnection ucon = (HttpURLConnection)url.openConnection();
InputStream is = ucon.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayBuffer baf = new ByteArrayBuffer(5000);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte) current);
}
image = BitmapFactory.decodeByteArray(baf.toByteArray(), 0, baf.toByteArray().length);
Using the debugger I confirmed the header data is loaded properly and is of GIF format. But once the decode function runs I'm left with a null value. My first guess is that I need to do a GIF -> PNG conversion. How can I do this? Are there other options?
The GifView class at http://code.google.com/p/android-gifview/ works well. That project is intended for animated gifs, however it works fine with static gifs as well.
So despite Android complaining about the GIF and decode failing I think the issue resided through other failures in my code. Changes I made which cause the code to now work.
Made sure I closed the input stream
Removed an incident which could cause infinite looping and thread generation (this was a failure on my parsing rules that pulled out the list of GIF files from an HTML source)
Ensured my storage of the Bitmap files in a List object were always properly casted before being used.
I found help and details through this posting which was an almost identical issue. There are links that follow through on the issue.
In the end I didn't touch my Bitmap or InputStream objects at all, the data was being received properly (as per my confirmation with the GIF header bytes) but was a failure of using the data afterwards.
I'm downloading a file in my android app and I want to display a progressbar. I need to know the total file size by using this method : here.
The problem is that I always get -1 despite the fact that it's set on the server and I can see it from my computer.
This is the code :
URL url = new URL(fileUrl);
URLConnection ucon = url.openConnection();
ucon.connect();
int length = ucon.getContentLength();
What is the problem ?
This is not related to Android, most probably it was not sent from the server..please use any sniffing tool to make sure the content length header is there.