I am desperatly trying to find a soulution for WiFi/GSM connection switch while uploading files via HttpUrlConnection.
I have seen people struggling with similar problems but still couldn't find a working solution.
Here is the code:
try {
/* Opening file stream and so... */
URL url = new URL(url_to_send);
connection = (HttpURLConnection) url.openConnection();
// Allow Inputs & Outputs
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
// connection.setConnectTimeout(1000); // doesn't have any effect
// connection.setReadTimeout(1000);
connection.setChunkedStreamingMode(maxBufferSize);
// Enable POST method
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data; charset=utf-8; boundary=" + BOUNDARY);
bytesAvailable = file_input.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// Read file
bytesRead = file_input.read(buffer, 0, bufferSize);
already_send += bytesRead;
while (bytesRead > 0) {
/* Updating some progressbar values */
/* HERE IS THE LINE WHERE THREAD HANGS */
output_byte.write(buffer, 0, bufferSize);
bytesAvailable = file_input.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = file_input.read(buffer, 0, bufferSize);
already_send += bytesRead;
}
output_byte.writeBytes(endBoundary);
output_byte.flush();
// Responses from the server (code and message)
serverResponseCode = connection.getResponseCode();
serverResponseMessage = connection.getResponseMessage();
} catch (FileNotFoundException e) {
Log.e(TAG, "Uploaded file not found");
e.printStackTrace();
} catch (MalformedURLException e) {
Log.e(TAG, "Malformed URL");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "Uploading problem: " + e.toString());
e.printStackTrace();
} finally {
/* closing connection & streams */
}
The problem is that whenever i switch from WiFi to GSM or from GSM to WiFi during the uploading loop the Thread freezes on the line marked in the code:
output_byte.write(buffer, 0, bufferSize);
I have tried using connection.disconnect() which should throw some kind of a IOException (it does in normal situations but not in this particular case)
I have tried using OutputStream.close() called from second thread but it also hangs the second (main UI) thread making me unable to even close the app.
I have been searching for something like connection.isAvailable() which I could call before the ouptut_byte.write but I couldn't find anything useful in HttpUrlConnection class.
I have tried monitoring the WiFi state but the uploading thread seems to be faster than wifi state change and it blocks before I am able to provide it the current wifi state change. (also tried the ConnectivityManager - no difference)
Finally I have tried to kill the thread by adding volatile flag to my Thread variable and using Thread.interrupt() - no effect. Thread is still alive and it's state is "RUNNING".
Have you got any ideas how Can I solve the problem? I would be really glad if I could catch any type of exception in this thread after is blocks.
I got into exactly the same situation as Shaar did. I have an application for the file upload on the server. On the HTC Desire A8181 (Android 2.2.2) when I turn off the mobile data connection (or switch it into wifi connection) when the appliaction writes into output stream (I am writing there 512 kB data blocks) it just blocks in there for approximately 16 minutes. After these 16 minutes I get an Socket Exception (which is not always the same).
When I try it on other device or on the same device (HTC Desire A8181) but on wifi connection, the exception is thrown right after the connection is closed. So I think it is a bug. And one solution that I think about is to upload in a Thread and when there is a change in connection (BroadcastReceiver) then let the thread die (for these 16 minutes) and start another Thread. It is not a nice solution because of living threads in the system but I don't know any other solution...
The example code is the same as in the beginning of this thread. I also tried both the Apache's HttpClient and the HttpUrlConnection.
You might need to use HttpClient: there are methods to set the socket timeout that may do what you want, which is in effect ending the connection and throwing some kind of exception when the network connection is broken by the switch.
Late response, but I used the following Q&A Android, How to handle change in network (from GPRS to Wi-fi and vice-versa) while polling for data to handle this. Have the upload in its own thread and on a network failover, restart the thread and upload.
Thank you Femi for your answer!
I've tried that solution today and it makes no difference at all.
I have used a custom multipartentity http://toolongdidntread.com/android/android-multipart-post-with-progress-bar/ found in here mixed with some apache libraries: httpclient, httpcore and httpmime
Here is the new code:
File uploaded_file = new File(mess.getPath());
CustomMultiPartEntity multipart = new CustomMultiPartEntity(new CustomMultiPartEntity.ProgressListener() {
#
Override
public void transferred(long num) {
Log.v(TAG, num * 100 / total_size + " !");
// updating progressbar
}
});
// adding some fields to multipart
multipart.addPart(file_field, new FileBody(uploaded_file));
total_size = multipart.getContentLength();
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(params, 5000);
HttpConnectionParams.setConnectionTimeout(params, 5000);
httpClient = new DefaultHttpClient(params);
httpContext = new BasicHttpContext();
httpPost = new HttpPost(url_to_send);
httpClient.getParams().setParameter("http.protocol.content-charset", "UTF-8");
httpPost.setEntity(multipart);
// Responses from the server (code and message)
HttpResponse response = httpClient.execute(httpPost);
serverResponseCode = response.getStatusLine().getStatusCode();
serverResponseMessage = response.getStatusLine().getReasonPhrase();
After WiFi / GSM
switch the Thread acts the same blocking the execution.I have also tried setting the timeouts this way:
httpclient.getParams().setParameter("http.socket.timeout", new Integer(1000));
httppost.getParams().setParameter("http.socket.timeout", new Integer(5000));
Still no effects...
Also tried using httpPost.abort() and httpClient.getConnectionManager().closeExpiredConnections() and httpClient.getConnectionManager().shutdown().
No effect at all, thread is not throwing any exceptions, it remains in state RUNNING but it is blocked and dead.
I think that there is nothing we can do about it.
Strange thing is that when I turn the wifi back on the thread "wakes up" and continue uploading but it is pointless if I would have to get back to wifi area in order to complete the uploading process.
PS. Both examples were tested on 3 real devices: Samsung Spica runing 2.1, Samsung Galaxy S running 2.2.1 and Motorola Defu runing also 2.2.1. While Motorola and Spica are having problems with the described situation the Galaxy S behaves differently - it throws an IOException which is something that I would expect from the other ones.
Related
I would like to know anyone has a sample code on how to use JsonWriter to post JSON data to WCF web service from Android?
I tested my WCF with Fiddler 4 (Composer with POST json data) and it gave me the correct return.
However, when I tested with my Android application which use JsonWriter, I didn't see any action on Fiddler (I set up Fiddler to check on my Android Emulator network traffic, by the way, I am testing on Android Emulator.).
With the same Android application, I can call GET with JsonReader to my WCF and get the correct reply.
Its just calling POST with JsonWriter got no response code or no action in Fiddler.
For JsonWriter (and Reader), I refer to Android developer >> JsonWriter
Here are my test results (Get and Post) with Emulator GET and POST.
Here are my test results with Fiddler direct POST.
First it gave me Result 307 then follow by 200.
And here is how I use JsonWriter to post (this block was from AsyncTask).
try
{
Log.d("TEST_JSON", "URL: " + params[0]);
URL url = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept","application/json");
conn.setRequestProperty("Content-Type","application/json");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setAllowUserInteraction(false);
// conn.connect();
OutputStream out = conn.getOutputStream();
JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
try
{
writer.setIndent(" ");
if(params[1].trim() == "ARRAY")
{
// Write array to WCF.
}
else if(params[1].trim() == "OBJ")
{
// Write object to WCF. <<== I am testing with one object.
writer.beginObject();
writer.name("ShipNo").value("SI10101");
writer.name("DoNo").value("DO230401");
writer.name("PartNo").value("102931-1201");
writer.name("Qty").value(1);
writer.name("ShipIn").value(1);
writer.endObject();
}
}
finally
{
writer.close();
out.close();
}
// If I enable below blocks, I will see 307 response code in Fiddler.
/*
conn.connect();
int responseCode = conn.getResponseCode();
Log.d("TEST_JSON", "Code: " + String.valueOf(responseCode));
*/
Log.d("TEST_JSON", "Finish sending JSON.");
conn.disconnect();
}
catch (IOException e)
{
Log.e("TEST_JSON",e.getMessage()); // <<-- No error from this try catch block.
}
I tried and still cannot figure out why JsonWriter didn't trigger to my WCF (I attached my WCF to my localhost service, only Fiddler direct POST will hit the break point in my WCF project while Android App didn't reach to it). I follow the exact example from Android Developer site though. I google and didn't find any site on using JsonWriter with OutputStreamWriter (I saw some post using StringWriter).
May I know where did my code wrong ?
Based on this StackOverFlow post WCF has a 'Thing' about URI, I managed to solve this issue.
All I need is to make sure my POST web service has URI Template ends with "Slash".
Example: http://10.72.137.98/myWebSvc/posvctFun/
Instead of http://10.72.137.98/myWebSvc/postFun
I'm using HttpResponseCache to enable the response caching (for web requests) in my android app, and the offline cache isn't working.
I'm doing the offline cache as the documentation tells me to do.
In my Application class, at the onCreate method, I'm turning on the the cache with:
try {
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
File httpCacheDir = new File(getCacheDir(), "http");
Class.forName("android.net.http.HttpResponseCache")
.getMethod("install", File.class, long.class)
.invoke(null, httpCacheDir, httpCacheSize);
} catch (Exception httpResponseCacheNotAvailable) {}
At my HttpConnection class I'm getting the JSON with the method:
private String sendHttpGet(boolean cacheOnly) throws Exception {
URL url = new URL(getUrlCompleta());
HttpURLConnection urlConnection = null;
String retorno = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
if(urlConnection == null)
throw new Exception("Conn obj is null");
fillHeaders(urlConnection, cacheOnly);
InputStream in = new BufferedInputStream(urlConnection.getInputStream(), 8192);
retorno = convertStream(in);
in.close();
urlConnection.disconnect();
if(retorno != null)
return retorno;
} catch(IOException e) {
throw e;
} finally {
if(urlConnection != null)
urlConnection.disconnect();
}
throw new Exception();
}
Where the convertStream method just parse a InputStream into a String.
The method fillHeaders put an token on the request (for security reasons) and if the parameter cacheOnly is true, then the header "Cache-Control", "only-if-cached" is added to the request header ( with the code: connection.addRequestProperty("Cache-Control", "only-if-cached");)
The cache works 'fine' (with minor strange behaviors) when there is connectivity and the app hit the web server just to see if there is a newer version of the JSON. When the web server answers "nothing changed", the cache works.
The problem is when I have no connectivity and use the header "Cache-Control", "only-if-cached". In this case, I receive a java.io.FileNotFoundException: https://api.example.com/movies.json. That is awkward, because the implementation code of the cache probably stores the response in a file named using a hash function on the request url, and not the url itself.
Does anyone knows what can I do or what is wrong with my implementation?
ps: Above, I said "probably using a hash function", because I was not able to found the implementation of the com.android.okhttp.HttpResponseCache object (the class that android.net.http.HttpResponseCache delegates cache calls). If someone found it, please tell me where to look at :)
ps2: Even when I add a max-stale parameter in the Cache-Control header, it still doesn't work.
ps3: I obviously tested it on api 14+.
ps4: Although I'm accessing an "https://" URL address, the same behavior occurs when the URL is just a normal "http://" address.
It turns out that the problem was with the max-age value of the Cache-control directive in the response given by my web server. It had the following value: Cache-Control: max-age=0, private, must-revalidate. With this directive, my server was saying to the cache that the response could be used from the cache even if it was 0 seconds old. So, my connection wasn't using any cached response.
Knowing that max-age is specified in seconds, all I had to do was change the value to: Cache-Control: max-age=600, private, must-revalidate! There it is, now I have a 10 minute cache.
Edit: If you want to use a stale response, with the max-stale directive of the request, you shouldn't use the must-revalidate directive in the response, as I did in my webserver.
I am uploading a file to server using the HttpsURLConnection class in Android. I am using PUT method. In normal circumstances it is working fine, but when I am attempting to write a file to the folder in a server, which does not have write permissions, the write method should fail and then when I use getResponseCode() I should recieve a 403 Forbidden response from the server. I have to use the 403 response to show a "No Permissions" message to the user.
Now, when the file size that I upload is less than the buffer size (16K), the write method returns and the getResponseCode returns 403. However, when the file size is bigger (which is usually the case), the program stucks in the write method and thus does not reach the getResponseCode at all. This is happening in all Android devices including the simulator.
Here is the code snippet:
HttpsURLConnection connection = getSSLChannel();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setConnectTimeout(connectionTimeout);
connection.setReadTimeout(readTimeout);
connection.setUseCaches(false);
connection.setRequestMethod("PUT");
connection.setFixedLengthStreamingMode(size);
connection.setRequestProperty("Connection", "Keep-Alive");
OutputStream out = connection.getOutputStream();
try{
int count =0;
long progress = 0;
byte[] chunkData = new byte[16384];
long fileSize = localFile.getLocalFileSize();
bis = getSourceBufferedStream();
while (((nRead = bis.read(chunkData)) != -1)) {
if(nRead < chunkData.length){
byte[] newBytes = Arrays.copyOf(chunkData, nRead);
out.write(newBytes, 0, nRead);
count++;
}else{
out.write(chunkData, 0, nRead);
count++;
}
}
out.flush();
}finally{
if(out != null){
try {
out.close();
out = null;
} catch (IOException e) {
e.printStackTrace();
}
}
int resCode = connection.getResponseCode();
System.out.println("Res Code :" + resCode);
}
I have put a debug pointer in the finally block. But the app never enters the finally block.
Some points:
1. The server does not support multipart upload.
2. There is no Separate web service to find out, if the folder has write permissions on the server.
3. In iOS this works fine. I am able to receive 403.
4. I have tried with Fiddler, which also gives me a 403.
5. Also, in fast networks (if the server is located in the local wifi domain), I am able to get 403 sometimes.
What am I doing wrong ? Please help !
Thanks.
My Android tablet application does not work with ICS due to a Login problem. When I looked at my code and ran it under debug mode on an ICS tablet, I see the problem but I don't understand it. The code functions correctly on all Honeycomb models that i have tested and in fact I have two tablets hooked up to my computer (one Samsung Galaxy Tab running 3.2, and a Motorola Xoom wifi running 4.0.3) and the code fails on ICS and works on HC.
The failure is a Socket Timeout exception. The timeout was 2000ms, but I upped it to 100000ms to test and it had no impact.
Using the browser on the ICS tablet, I can go to the URL and it responds, so it doesn't appear to be network related.
I am running on a background thread using AsyncTask.
Slurp just takes all of the input from the InputStream and using StringBuilder creates a string representation. Its not actually useful in this request but I added it to see what the server was replying with.
I am POSTing to the page the same way a user authenticates using the form, which is why I am using x-www-form-urlencoded.
Again, this code functions perfectly on Honeycomb but fails on ICS.
The code makes a connection but fails when it asks for a response from the server, almost like the server is still waiting for something... anyway, here is the code:
static public String authenticate(String service_url, String username, String password) throws IOException {
if (username == null || password == null)
throw new IOException();
String charset = "UTF-8";
String query = String.format("Email=%s&Password=%s",URLEncoder.encode(username, charset),URLEncoder.encode(password, charset));
byte [] data = query.getBytes(charset);
URL url = new URL(service_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Length", Integer.toString(data.length));
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setReadTimeout(5000); // 2 second timeout.
try {
connection.connect();
DataOutputStream pw = new DataOutputStream (connection.getOutputStream());
pw.writeBytes(query);
pw.flush();
pw.close();
int code = connection.getResponseCode(); //SOCKET TIMEOUT HERE
if (code == 200 || code == 302)
{
InputStream is = connection.getInputStream();
String value = slurp(is);
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
if (cookies == null)
throw new IOException();
for (String cookie : cookies) {
if (cookie.startsWith("cpms")) {
cookieTime = new DateTime(); //crazy but the expires time in the cookie is not actually accurate.
return cookie; // this is the only correct path out.
}
}
}
else
Logger.e(StaticUtils.class, "Invalid response code while logging in: " + code);
}
catch (IOException ioe)
{
Logger.e(StaticUtils.class, ioe);
throw ioe; // log it and then throw it back.
} finally {
connection.disconnect();
}
return null;
}
What is the maximum size of android Post method? When I am getting responses from server, parts of the message are missing. I think it may have reached the maximum size of post method.
If there is no limit for post method, did I need to change my server specification for this?
I had the same problem, I used HttpPost and the response got from server but a part of data missed because of their very big size. That's why I used an other way : HttpURLConnection with OuputStream to send request to the server and BufferedReader/InputStream to get responses.
HttpURLConnection my_httpConnection = (HttpURLConnection) new URL("https://integrator-ut.vegaconnection.com/Authentication.svc?wsdl").openConnection();
my_httpConnection.setRequestMethod("POST");
my_httpConnection.setDoInput(true);
my_httpConnection.setDoOutput(true);
my_httpConnection.setRequestProperty("Content-type", "text/xml; charset=utf-8");
OutputStream my_outPutStream = this.my_httpConnection.getOutputStream();
Writer my_writer = new OutputStreamWriter(my_outPutStream);
my_writer.write(YOUR_REQUEST); //YOUR_REQUEST is a String
my_writer.flush();
my_writer.close();
BufferedReader my_bufferReader = new BufferedReader(new InputStreamReader(this.my_httpConnection.getInputStream()));
char[] buffer = new char[10000];
int nbCharRead=0;
try
{
while((nbCharRead = my_bufferReader.read(buffer, 0, 10000)) != -1)
{
/* Your treatement : saving on a file/arraylist/etc
}
}
Theoretically there isn't a limit. The POST response size is limited to Java VM Heap size which is device independent. It is probably more than your post response consumes.
How do you verify that part of your response is missing? If you print it out with LogCat or view it in debug mode, then you can see only the start of the message ending with three dots (all of the message is there, it isn't just displayed to you).