Android keeping connection alive to receive constant data update from server - android

i hope you can help me with my problem. I have to instantiate an http connection and do a get call to the server.
After this call i have to check periodically if the server sent me some data (the connection must not be closed after the first data received). If it does, then i have to parse this data and pass it to an activity and wait again.
My problem is to understand if i'm doing correctly this thing. Here my code
try {
URL url = getUrl();
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConnection = (HttpURLConnection) urlConn;
httpConnection.setRequestMethod("GET");
httpConnection.connect();
while(true) {
sleep(5000);
int responseCode = httpConnection.getResponseCode();
if(responseCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = httpConnection.getInputStream();
String response = null;
if(inputStream.available() > 0) {
long length = inputStream.available();
byte[] bytes = new byte[(int) length];
inputStream.read(bytes);
response = new String(bytes, "UTF-8");
}
}
}
} catch(Exception e) {
e.printStackTrace();
}
getUrl() function gives me the complete url that i have to call.
Then i connect through httpConnection.connect();.
while i'm iterating is the connection already opend and capable of receiving incoming data?
I apologize for my english. Thanks a lot
Francesco

From the official android documentation
Uses of this class follow a pattern:
Obtain a new HttpURLConnection by calling URL.openConnection() and
casting the result to HttpURLConnection.
Prepare the request. The
primary property of a request is its URI. Request headers may also
include metadata such as credentials, preferred content types, and
session cookies.
Optionally upload a request body. Instances must be
configured with setDoOutput(true) if they include a request body.
Transmit data by writing to the stream returned by getOutputStream().
Read the response. Response headers typically include metadata such as
the response body's content type and length, modified dates and
session cookies. The response body may be read from the stream
returned by getInputStream(). If the response has no body, that method
returns an empty stream.
Disconnect. Once the response body has been
read, the HttpURLConnection should be closed by calling disconnect().
Disconnecting releases the resources held by a connection so they may
be closed or reused.
As you see from this quote, if you need to send some data to a host you just need to call setDoOutput(true) and use OutputStream of your connection to send data to the host. In this case you use "POST" request method after you called setDoOutput(true). If you don't need to send data, but just to connect to the host and retrieve data, you also don't need to call setRequestMethod("GET"), because it's default request method.
When you connect to the host, just use InputStream to get data from it.

Related

JsonWriter POST not working in Android to WCF web service

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

How to Use Both HTTPS and HTTP to parse JSON data in Android?

I followed this to Parse Json In Android
I have Successfully Done it with HttpData handler..
Here I am Successfully Posting Data to server and Getting Response..
Now I want to Use this same in the Part of HTTPS..
Can Any one suggest me How to do this Without Major Changes in my code.. Because In my application I am doing this for more activities.. Please Suggest me to Use HTTPs in my code..
I will provide Additional Info... Depending Responses...
Update
In my code I have Changed HttpURLConnection to HttpsURLConnection
Please suggest me How to through this error In my code..
Update 1
I have Changed Certificate on server side.. Now its working On Https..
But Now,
I want to Use HTTP and HTTPS Both in one app Depending on Client Requirement So here now its worked with Https....
But I also need to work with Http
In my Code Can any any one suggest me...I want I should Work with Https and Http Both In one App.
to use both HTTP and HTTPS, you need to have the 2 methods (i think you already have them)
GetHTTPData(String urlString)
GetHTTPSData(String urlString)
now in HTTPDataHandler class (where you have both methods above)
you need to create a 3rd method GetDataFromUrl(), that will check URL and decide which method to use (http or https)
public String GetDataFromUrl(String url){
if(url.toLowerCase().startsWith("https")){
//HTTPS:
return GetHTTPSData(url);
}else{
//HTTP:
return GetHTTPData(url);
}
}
now in the AsyncTask class ProcessJSON
replace this line stream = hh.GetHTTPData(urlString);
with this one stream = hh.GetDataFromUrl(urlString);
if you don't want to add that 3rd method in HTTPDataHandler, just use the if-statement in ProcessJSON at doInBackground() to call either one of the 2 methods (http or https)
You can use HttpsURLConnection, replace HttpURLConnection by HttpsURLConnection .
public String GetHTTPData(String urlString){
try{
URL url = new URL(urlString);
HttpsURLConnection urlConnection =(HttpsURLConnection)url.openConnection();
// Check the connection status
if(urlConnection.getResponseCode() == 200)
{
// if response code = 200 ok
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
// Read the BufferedInputStream
BufferedReader r = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
sb.append(line);
}
stream = sb.toString();
// End reading...............
// Disconnect the HttpURLConnection
urlConnection.disconnect();
}
else
{
// Do something
}
}catch (MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally {
}
// Return the data from specified url
return stream;
}
What I understand is in your server side, they used self signed SSL certificate. So you have to install that certificate in your android device also. Settings > Security > install form storage.But for production build you have to buy ssl certificate from CA Authorities.
Hope this will solve your problem.
Remove HttpDataHandler lines in doInBackground use HttpUrlConnection directly in doInBackground or use HttpUrlConnection in JSONparse class to post params to server follow this tutorial to post params Website

Google provides 2 different examples of HttpURLConnection usage - Should we call HttpURLConnection's disconnect, or InputStream's close?

Google is providing 2 different examples of HttpURLConnection usage.
Calling InputStream's close
http://developer.android.com/training/basics/network-ops/connecting.html
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
Log.d(DEBUG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
}
Calling HttpURLConnection's disconnect
http://developer.android.com/reference/java/net/HttpURLConnection.html
URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
finally {
urlConnection.disconnect();
}
}
For resource leakage and performance consideration (Need not to setup network connection from ground up, as my app will communicate with same server most of the time), should we
Call HttpURLConnection's disconnect only.
Call the InputStream's close only.
Call both HttpURLConnection's disconnect & InputStream's close (Haven't seen such official example so far).
According to Oracle's Java, calling the disconnect() may close the underlying socket connection, if the connection is idle (not fetching anything). This "indicates that other requests to the server are unlikely in the near future" using that same HttpUrlConnection instance. You will have to reopen a new socket connection by creating another HttpUrlConnection.
However, Google has modified HttpUrlConnection so that socket connections can be reused. In Android, the underlying Socket used by a HttpUrlConnection may be persisted and reused for multiple requests. If you call disconnect after completing your request, it may send the socket to a pool containing other idle connections, ready to be reused. The system does this to reduce latency.
From the documentation:
Unlike other Java implementations, this will not necessarily close
socket connections that can be reused. You can disable all connection
reuse by setting the http.keepAlive system property to false before
issuing any HTTP requests.
So you should call disconnect to release resources (streams and sockets), but some of the resources released may be reused (i.e. the socket will go into a pool of idle sockets, ready for the next HttpUrlConnection instance to reuse it).
As for why the first example did not call disconnect(), that is the Java SE way of reusing the connection (I think, it's been a while). What the author did was manually close the InputStream of the connection and left the socket open and idle for reuse. The second example shows the correct way on Android.
In summary: calling disconnect() on Android will close any InputStream or OutputStream used for the connection and may send the socket used for the connection to a pool, ready to be reused for other requests. So, if you call disconnect(), there is no need to call InputStream#close().
Both close and disconnect method release the connection if not yet released with following 2 differences:
close method throws IOException. Hence in first example, the enclosing method downloadUrl's signature has throws IOException. Where as disconnect method does not throw any exception.
Calling close method ensures that any future references made to the closed connection such as read operation, will result in IOException.
The most important design fact about Android's HttpURLConnection implementation is :
Connection is held when the last byte of the response is consumed.
When the response if fully read, the connection is released and will
be pooled immediatetly.
You can see in the below image, variable connection & connectionReleased are set to null and true respectively, as soon as all data is read. In this case calling disconnect makes no difference, calling close just ensures future calls on closed connection throws IOException.
If data is still available on the InputStream, close or disconnect has to be called to release the connection explicitly. In this case the connection is not reused and underlying socket connection is also closed. This is done in anticipation that more data may arrive on the InputStream.
You can see in below code snippets, both disconnect and close will finally invoke httpEngine.release(false), which will close the connection without adding to the connection pool.
disconnect implementation:
#Override public final void disconnect() {
// Calling disconnect() before a connection exists should have no effect.
if (httpEngine != null) {
httpEngine.release(false);
}
}
close implementation:
#Override public void close() throws IOException {
if (closed) {
return;
}
closed = true;
if (bytesRemaining != 0) {
unexpectedEndOfInput();
}
}
unexpectedEndOfInput implementation:
protected final void unexpectedEndOfInput() {
if (cacheRequest != null) {
cacheRequest.abort();
}
httpEngine.release(false);
}
release implementation:
public final void release(boolean reusable) {
// If the response body comes from the cache, close it.
if (responseBodyIn == cachedResponseBody) {
IoUtils.closeQuietly(responseBodyIn);
}
if (!connectionReleased && connection != null) {
connectionReleased = true;
// We cannot reuse sockets that have incomplete output.
if (requestBodyOut != null && !requestBodyOut.closed) {
reusable = false;
}
// If the headers specify that the connection shouldn't be reused, don't reuse it.
if (hasConnectionCloseHeader()) {
reusable = false;
}
if (responseBodyIn instanceof UnknownLengthHttpInputStream) {
reusable = false;
}
if (reusable && responseBodyIn != null) {
// We must discard the response body before the connection can be reused.
try {
Streams.skipAll(responseBodyIn);
} catch (IOException e) {
reusable = false;
}
}
if (!reusable) {
connection.closeSocketAndStreams();
connection = null;
} else if (automaticallyReleaseConnectionToPool) {
HttpConnectionPool.INSTANCE.recycle(connection);
connection = null;
}
}
Summary :
Connection is automatically released to the connection pool as soon as the last byte of the response is consumed.
If the IOException will be handled by method operating on InputStream, use disconnect. If the IOException will be handled by the caller of the method operating on InputStream, use close. Remember close ensures IOException is thrown when read operation is performed on closed connection in future.

FileNotFoundException when using the offline cache of HttpResponsecache

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.

Android app remain logged into website, cookies? Session?

I'm looking to make an android app that is basically a custom view of a text based gaming website. I know how to do HttpPosting and such, so sending login information is relatively simple. But my question is, how would I go about then navigating the site? I've never really worked with sessions and cookies on the client side. Is a cookie the right way to implement this? How do I pass the info back to the server when accessing subsequent pages?
I hope that makes sense
Generally, in Java HttpURLConnection you can set / get a cookie this way (here is the whole connection process). The code below is in my ConnectingThread's run(), from which all the connecting activity classes inherit. All share common static sCookie string which is sent with all the requests. Therefore you can maintain a common state like being logged on / off:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//set cookie. sCookie is my static cookie string
if(sCookie!=null && sCookie.length()>0){
conn.setRequestProperty("Cookie", sCookie);
}
// Send data
OutputStream os = conn.getOutputStream();
os.write(mData.getBytes());
os.flush();
os.close();
// Get the response!
int httpResponseCode = conn.getResponseCode();
if (httpResponseCode != HttpURLConnection.HTTP_OK){
throw new Exception("HTTP response code: "+httpResponseCode);
}
// Get the data and pass them to the XML parser
InputStream inputStream = conn.getInputStream();
Xml.parse(inputStream, Xml.Encoding.UTF_8, mSaxHandler);
inputStream.close();
//Get the cookie
String cookie = conn.getHeaderField("set-cookie");
if(cookie!=null && cookie.length()>0){
sCookie = cookie;
}
/* many cookies handling:
String responseHeaderName = null;
for (int i=1; (responseHeaderName = conn.getHeaderFieldKey(i))!=null; i++) {
if (responseHeaderName.equals("Set-Cookie")) {
String cookie = conn.getHeaderField(i);
}
}*/
conn.disconnect();

Categories

Resources