I am working on an Android app that needs to read a line from a web page right when it starts. I am doing this with the following code:
try{
URL url = new URL("http://www.example.com");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
line = reader.readLine();
}
catch(Exception e){
e.printStackTrace();
}
It is working fine, but sometimes the connection or the server are slow, and the app freezes or crashes.
I want to put a timeout of 5 seconds, and should it reach that timeout I want to show a toast to the user saying the network is busy, asking him to try again later.
I tried the HttpURLConnection setConnectTimeout() method but it didn't work.
Any clues how I can achieve this? Thanks forehand.
Probably better solution if you setConnectionTimeout to 5 sec, catch SocketTimeoutException and show Toast from there. When you set ConnectionTimeout to some value and connection didn't get response code will throw SocketTimeoutException. Here you can catch it and call handler to show a toast in UI. Finally will close the connection and release memory.
class MyHttpClient extends DefaultHttpClient {
#Override
protected ClientConnectionManager createClientConnectionManager() {
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https",
mSSLSocketFactory != null
? mSSLSocketFactory
: SSLSocketFactory.getSocketFactory(),
443));
return new SingleClientConnManager(getParams(), registry);
}
MyHttpClient httpClient = new MyHttpClient();
// set http params
HttpParams params = httpClient.getParams();
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, new Integer(30000));
params.setParameter(CoreConnectionPNames.SO_TIMEOUT, new Integer(30000));
httpClient.setParams(params);
....
httpClient.execute(httpUriRequest)
Consider using AndroidHttpClient class instead, it has nice preset timeouts so you wouldn't have to do anything.
Related
I have developed an Android app and it is running smooth. Not yet into production. This app communicates with the server using REST services. Now
arrived an issue. As of now I am using HTTP to communicate with the server and get the info back from the server.
I wish to have the data transmission more secured. Can anyone please shed some light on how to accomplish this? I am clueless about HTTPS and implementing it in Android.
Any pointers and links will be helpful like how to start. Do I need to purchase certificates and get it signed.
Thanks
I fairly certain you don't need to do anything special for HTTPS in Android, just change your url to start with "https". new URL(url).openConnection() should work fine with that. If you're using Apache's HttpClient stuff, you might need to define your own client. Below is code I have from a project I'm currently working on.
public static DefaultHttpClient makeHTTPClient() {
BasicHttpParams mHttpParams = new BasicHttpParams();
// Set the timeout in milliseconds until a connection is established.
// The default value is zero, that means the timeout is not used.
int timeoutConnection = 15000;
HttpConnectionParams.setConnectionTimeout(mHttpParams, timeoutConnection);
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 20000;
HttpConnectionParams.setSoTimeout(mHttpParams, timeoutSocket);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
registry.register(new Scheme("https", sslSocketFactory, 443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(mHttpParams, registry);
DefaultHttpClient defaultHttpClient = new DefaultHttpClient(cm, mHttpParams);
return defaultHttpClient;
}
I am trying to start a connection with a microsoft exchange server web access portal. The idea is my program initially opens a web view, and then continually refreshes the page using a BroadcastReceiver. I am aware that ordinarily, exchange web-based email access can be done through a protocol, but I'm trying this anyhow. The timing for the refresh works fine, but the problem is that although I open the connection with a WebView, I'm then doing the refresh with an HttpClient. I am passing the cookies after login from the WebView to the HttpClient successfully, and opening the connection for SSL, yet the web server is kicking back. This works fine in a browser, so I'm kinda lost.
EDIT: Ok, this is a more basic HTTP request question. All of the code that handles the httpGet is setup in the constructor. I don't even make a new HttpClient. Now, This is a BroadcastReceiver, and it responds to a timed message every 60 seconds or so to make the request, but every time the code finishes, the thread seems to die, and every time the timer goes off, the blank constructor is called. When coding an http GET request like this, do I need to be recreating the HttpClient every time? or is my using static variables that hold their value an ok way of doing it? The URL and cookies survive from one call to the next. What else am I missing? The very first call to this thing doesn't seem to work either, so I still think it's that I'm doing SSL wrong. Again, this site works just fine in a web browser, so I'm not emulating the request properly.
This is the constructor code:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
// RESTORE THE COOKIES!!!
cookieJar = new BasicCookieStore();
localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieJar);
cookie = extras.getString("cookies");
String[] cookieCutter = cookie.split(";");
for (int i=0; i < cookieCutter.length; i++)
{
String[] values = cookieCutter[i].split("=");
BasicClientCookie c = new BasicClientCookie(values[0], values[1]);
c.setDomain(MAIL_WEB_SERVER);
cookieJar.addCookie((Cookie)c);
}
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
// Create local HTTP context
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", socketFactory, 443));
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(params, registry);
httpClient = new DefaultHttpClient(mgr, params);
httpClient.setCookieStore(cookieJar);
// Set verifier
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
And this is the refresh code:
BasicCookieStore cookies = (BasicCookieStore)localContext.getAttribute(ClientContext.COOKIE_STORE);
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpClient.execute(httpGet, localContext);
So where does this leave me? I get a Error Code: 500 Internal Server Error. The server denied the specified Uniform Resource Locator (URL). Contact the server administrator. (12202)
I am moving the cookies over, and accepting all certificates...is there anything else from a browser session with an SSL connection and a login that I need to be passing along for the httpGet request?
I have an Android app that sends commands to a robot via HTTP. (The robot server is written in Python using the BaseHttpServer class.) I'm setting up my connection in the app correctly as far as I can tell, but most requests fail to return and if they do there is a very long delay. Here is the initialization code:
private void setupHttpStuff() {
HttpParams params = new BasicHttpParams();
ConnManagerParams.setMaxTotalConnections(params, 10);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(), 1504));
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
this.HttpClient = new DefaultHttpClient(cm, params);
}
The GET request takes place in an AsyncTask and the code looks like:
HttpContext localContext = new BasicHttpContext();
HttpGet httpGet = new HttpGet(url);
mClient.execute(httpGet, localContext);
Where mClient is a variable shared from the Activity.
By using Log.d messages I've determined that the first two requests work fine but then all subsequent requests (in separate AsyncTasks) hang and never return from execute. I've also tried to manually create a Socket and send a HttpRequest:
Socket socket = new Socket("192.168.1.45", 1504);
HttpParams params = new BasicHttpParams();
DefaultHttpClientConnection conn = new DefaultHttpClientConnection();
conn.bind(socket, params);
HttpRequest request = new BasicHttpRequest("GET", "/");
conn.sendRequestHeader(request);
HttpResponse response = conn.receiveResponseHeader(); // Hangs here
conn.receiveResponseEntity(response);
socket.close();
But this hangs on the conn.receiveResponseHeader() line and the server never sees the request.
The Python server works fine with requests from a browser, just not with my Android app. I've tried this single-threaded as well in the main UI but the same effect occurs.
EDIT
Here is the Python code that causes the problems:
class MyHand(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
qv = parse_qs(get_qs(urlparse(self.path)))
if("method" not in qv):
self.send_nack()
elif(qv["method"][0] == "drive"):
createbot.Drive(int(qv["velocity"][0]), int(qv["radius"][0]))
self.send_ack()
else:
self.send_nack()
def send_ack(self, content_type='text/html'):
self.send_response(200)
self.send_header('Content-type', content_type)
self.end_headers()
def send_nack(self):
self.send_response(500)
self.send_header('Content-type', 'text/html')
self.end_headers()
This may be related to keep-alive. Android sends Connection: Keep-Alive and Python sends back a Connection: close. I've looked at the HTTP headers that are being sent from Firefox and HttpClient and there is no difference (other than Firefox sending some additional Accept headers). Somehow Android is expecting the Python server to send something other than HTTP/1.1 200 OK\nContent-type: text/html and therefore keeping the socket open. I ended up writing my own basic HTTP server and client.
Python code:
class SingleTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
parts = data.rsplit(" ")
if (parts[0] != "GET"):
self.request.close()
return
url = parts[1].split("?")[1]
qv = parse_qs(url)
...
Android:
Socket socket = null;
PrintWriter output = null;
try {
socket = new Socket(this.ipAddress, this.portNo);
output = new PrintWriter(new BufferedWriter
(new OutputStreamWriter(socket.getOutputStream())), true);
output.println("GET " + url);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
if (output != null)
output.close();
}
First off, I'm pretty newb at this. I'm new to Android, to asp, to javascript, to http even.
I'm trying to build an Android app that allows me to login to my school's website and pull data off it, ultimately I hope to do something like insert my timetable's data into Android's calendar entries. However I'm having trouble logging in.
Here's the website:
https://sso.wis.ntu.edu.sg/webexe88/owa/sso_login2.asp
What I'm doing currently is doing a http POST to the above-mentioned URL and I'm hoping to be redirected to hhttps://wish.wis.ntu.edu.sg/pls/webexe/aus_stars_check.check_subject_web2 which will display my timetable.
So far my code is as follows after viewing the webpage's source and searching quite a bit on the Internet:
private void start_login(String[] array) {
// TODO Auto-generated method stub
Toast.makeText(this, "Logging in...", Toast.LENGTH_LONG).show();
WebView wv = new WebView(this);
this.setContentView(wv);
try {
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
nameValuePairs.add(new BasicNameValuePair("UserName", <my username here>));
nameValuePairs.add(new BasicNameValuePair("PIN", <my password here>));
nameValuePairs.add(new BasicNameValuePair("Domain", "STUDENT"));
nameValuePairs.add(new BasicNameValuePair("p2", "https://wish.wis.ntu.edu.sg/pls/webexe/aus_stars_check.check_subject_web2"));
wv.loadData(CustomHttpClient.executeHttpPost(URL, nameValuePairs), "text/html", "utf-8");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}// end start_login
That's the login function.
The CustomHttpClient I'm using is thanks to this guy: http://www.newtondev.com/2010/07/27/making-http-requests-using-google-android/
So far I'm not getting any results. What am I doing wrong? Am I missing values in the ArrayList, or have I got the URL all wrong?
Below code handles https and gives httpsclient for https url .. you need httpsclient to make request to https urls.
Might below code is of help to you:
public DefaultHttpClient getClient()
{
DefaultHttpClient ret = null;
//sets up parameters
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf-8");
params.setBooleanParameter("http.protocol.expect-continue", false);
//registers schemes for both http and https
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
registry.register(new Scheme("https", sslSocketFactory, 443));
ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(params, registry);
ret = new DefaultHttpClient(manager, params);
return ret;
}
I have a class for Httpclient. The same instance is used throughout the application. So if the client == null it should create one else it will return the existing instance. Everything works until i try to release the resource on exit by doing: client.getConnectionManager().shutdown();....after this i am not able to login again. It gives Sysem error saying : connection pool shutdown. Heres the class:
public class HttpClientFactory {
private static DefaultHttpClient client;
public synchronized static DefaultHttpClient getThreadSafeClient() {
if (client != null)
return client;
client = new DefaultHttpClient();
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
HttpParams params = new BasicHttpParams();
SingleClientConnManager mgr = new SingleClientConnManager(params, schemeRegistry);
client = new DefaultHttpClient(mgr, params);
return client;
}
}
After this i simply run client.getConnectionManager().shutdown(); onBackPressed(), can somebody please help me
Ok..i got the solution...I was not setting it back to null and connection shutdown is suppose to be done in the same global class...
I have faced similar issue, but set
client.getConnectionManager().shutdown()
client.getConnectionManager() = null
could not resolved the problem.
Finally, I found out that, I did close all connections in onPostExecute, which is incorrect, so after moving them into doInBackground, it works.
Hope this helps someone!