Optimizing HTTP requests in android - java

I have noticed that my http requests tend to take alot of time compared apps communicating with same server. It makes my app feel sluggish and I was wondering if there is a better way of making these requests and updating the UI.
At the moment I use this method to make post requests
public String postRequest(List<NameValuePair> nameValuePairs, String method_name) {
String result = "";
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://www.mysite.com/api/"+method_name);
httppost.setHeader("Accept", "application/json");
httppost.setHeader("Authorization", "Basic somestuff");
try {
// Add your data
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
BufferedReader rd = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
result = rd.readLine();
return result;
} catch (ClientProtocolException e) {
} catch (IOException e) {
}
return null;
}
And in my UI thread (i.e my Fragment classes) I use this in an Async Task like this
class MakeRequest extends AsyncTask<Integer, Integer, String> {
protected String doInBackground(Integer... counter) {
String result = "";
String method_name = "";
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("id", value));
nameValuePairs.add(new BasicNameValuePair("name", name));
method_name = "petition/setPetition";
result = fixr.postRequest(nameValuePairs, method_name);
JSONObject jsonFile = new JSONObject(result);
if(!jsonFile.has("error")){
//Parse JSON using GSON
return "success";
}else{
return jsonFile.getString("error");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String jsonResult) {
try {
if(jsonResult != null){
//update UI
}else{
//Error message
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'd like to optimize this so users can have a really smooth experience on my application. I'm open to using third party http libraries or is there also an argument against using AysncTasks and maybe the runOnUiThread() instead.

Volley Library is better, http, https etc.
https://developers.google.com/live/shows/474338138
very mini sample here:https://github.com/ogrebgr/android_volley_examples/blob/master/src/com/github/volley_examples/Act_SimpleRequest.java

Try Volley mate! I changed from AsyncTasks to Volley library and i am pretty pleased from the overall experience!
Volley Library

Related

No response from HTTP request

i have written an android app which post data to my database. The app should access an webservice which post the data to the database. the webservice works fine. ive testet it with my browser, he is already on the server. now i want my app to execute the webservice. but that doesnt work. My debugger doesnt work too so im not able to debug. here is my code to for accessing the webservice. any ideas??
public class PostBlog extends AsyncTask<String, Void, String> {
String BlogURL;
public PostBlog(String insertBlogURL) {
BlogURL = insertBlogURL;
}
#Override
protected String doInBackground(String... params) {
postBlogData(BlogURL);
return null;
}
public void postBlogData(String url) {
String result = "";
//the year data to send
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("year", "1980"));
//http post
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
result = sb.toString();
} catch (Exception e) {
//(TextView)rootView.findViewById(R.id.question)
Log.e("log_tag", "Error converting result " + e.toString());
}
}
}
The Class is called from my main Activity by
new PostBlog(insertBlogURL).execute("");
Is there another easier way to execute my ".jsp?asdd=sdsd" file on the server?
Thanks for your ideas.
Instead of doing :
new PostBlog(insertBlogURL).execute("");
Change your constructor and retrieve the url from the doInBackground method, by doing params[0]
Then initiate the download like this
PostBlog blogPoster = new PostBlog();
try {
blogPoster.execute(insertBlogURL);
} catch (InterruptedException e) {} catch (ExecutionException e) {}
I should say this is a modified snippet of code from my own project, so it might not work exactly the way you expect.

Android - Keep user logged between activities

In my app I've a first activity that allow you to login to a web service. If the server allow user to connect it should call a TabHost activity. In TabHost activity I've 3 different activity:
HomeActivity: it display just a webview
HistoryActivity: it should display a ListView in which I insert the notification history
SettingsActivity: it display some settings of the app
In HistoryActivity I've to call to a web service to download a list of the notification history. To call this service I've to keep user logged, how I can do that?
I'm using the following code to connect to history service:
public void postData(String url) {
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
try {
StringEntity jsonSend = new StringEntity("{\"history\": true}");
httpPost.setHeader("Content-type", "application/json; charset=UTF-8");
httpPost.setEntity(jsonSend);
HttpResponse response = httpClient.execute(httpPost);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
StringBuilder builder = new StringBuilder();
for (String line = null; (line = reader.readLine()) != null;) {
builder.append(line).append("\n");
}
String json = builder.toString();
Log.d("HISTORY", json);
}
} catch (JSONException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
I used it for login and it works great. To connect to login service I use the following code:
public void postData(final String username, final String password) {
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("username", username));
nameValuePairs.add(new BasicNameValuePair("password", password));
nameValuePairs.add(new BasicNameValuePair("pollingId", "XXXXXXXXX"));
httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpClient.execute(httpPost);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
StringBuilder builder = new StringBuilder();
for (String line = null; (line = reader.readLine()) != null;) {
builder.append(line).append("\n");
}
String json = builder.toString();
JSONObject jsonObject = new JSONObject(json);
Boolean success = jsonObject.getBoolean("success");
if (success == true) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "ACCESSO EFFETTUATO!", Toast.LENGTH_LONG).show();
Intent i = new Intent(MainActivity.this, TabBarActivity.class);
startActivity(i);
}
});
}
} catch (ClientProtocolException e) {
Log.d("CLIENT EXCEPTION", "ERRORE: " + e);
}catch (IOException e) {
Log.d("I/O EXCEPTION", "ERRORE: " + e);
} catch (JSONException e) {
e.printStackTrace();
}
}
If I use it to get notification history the JSON said me that user is not logged. How I can fix it? Thank you
UPDATE
I tried to follow your suggestions, but I've the same result. The modified code is the follow:
MainActivity.java
public void postData(final String username, final String password) {
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("https://extranet.gruppotesta.it/srv/at-brain/login.jsp");
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("username", username));
nameValuePairs.add(new BasicNameValuePair("password", password));
nameValuePairs.add(new BasicNameValuePair("pollingId", "XXXXXXX"));
httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpClient.execute(httpPost);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
StringBuilder builder = new StringBuilder();
for (String line = null; (line = reader.readLine()) != null;) {
builder.append(line).append("\n");
}
String json = builder.toString();
JSONObject jsonObject = new JSONObject(json);
Boolean success = jsonObject.getBoolean("success");
if (success == true) {
Header[] cookie = response.getHeaders("cookie");
SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("cookie", cookie.toString());
editor.commit();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "ACCESSO EFFETTUATO!", Toast.LENGTH_LONG).show();
Intent i = new Intent(MainActivity.this, TabBarActivity.class);
startActivity(i);
}
});
}
} catch (ClientProtocolException e) {
Log.d("CLIENT EXCEPTION", "ERRORE: " + e);
}catch (IOException e) {
Log.d("I/O EXCEPTION", "ERRORE: " + e);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
HistoryActivity.java
public void postData(String url) {
HttpClient httpClient = new DefaultHttpClient();
SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
String stringCookie = sharedPreferences.getString("cookie", null);
BasicCookieStore cookieStore = new BasicCookieStore();
Cookie cookie = new BasicClientCookie("login", stringCookie);
cookieStore.addCookie(cookie);
HttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
HttpPost httpPost = new HttpPost(url);
try {
StringEntity jsonRequest = new StringEntity("{\"history\": true}");
httpPost.setEntity(jsonRequest);
httpPost.setHeader("Content-type", "application/json; charset=UTF-8");
httpPost.setHeader("Cookie", cookie.toString());
HttpResponse response = httpClient.execute(httpPost, localContext);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
StringBuilder builder = new StringBuilder();
for (String line = null; (line = reader.readLine()) != null;) {
builder.append(line).append("\n");
}
String json = builder.toString();
Log.d("HISTORY", json);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
When I try to run the app the server answer me with error "User not logged". What's wrong in my code?
When you first login, the web service must provide you with some sort of access token - a valid user id perhaps. You need to store and retrieve this user id in a SharedPreference. This user id must be passed as a parameter to all subsequent web services to indicate that the user is indeed logged in.
The official tutorial for how to get and set a SharedPreference is here.
You can try to use CookieStore to store a cookie with the login credentials. Take a look at this link: Http cookie store in Android.
Does your service give back a session cookie after successful login? If so you should store the cookie that the service issues after a login (in the Set-Cookie header in the response from the server) and set this cookie for any future HTTP requests.
httpPost.setHeader("Cookie", COOKIE_FROM_SERVER_AFTER_LOGIN);
You could use a CookieStore as well to help you store the cookies from HTTP requests - this will make cookie and session management easier.
UPDATE
Server headers won't include a Cookie header but a Set-Cookie header (because the server is instructing your useragent/browser/client to set a cookie and it's this cookie that will be included in your Cookie headers) so change this line:
response.getHeaders("cookie");
to
response.getHeaders("set-cookie");
UPDATE:
Your revision is now pulling out the correct Set-Cookie header but you are incorrectly storing the value (you are storing the entire header as the cookie when all you need is the value).
if (success == true) {
Header[] cookies = response.getHeaders("set-cookie");
//at this point cookies looks like this:
//Set-Cookie: JSESSIONID=84DB43CE8CABC52EBDF777BC0EA96D0F; Path=/; Secure
//if you just do cookies.toString() like you were doing before it will also include
//the cookie header which will create an incorrect cookie value.
//you just need the value of the Set-Cookie header and store that as your cookie
if (cookies.length > 0){
//it is very possible for a server to return more than one Set-Cookie header so the proper
//way would be to iterate through all of the values and string them together
//in the correct synatax of
//so you might want to store all of the values as a list in sharedPreferences
//and let your cookie store put them all in the request for you
String finalCookie = "";
for (Header header: cookies){
//access the value from the Header object and nothing else
//JSESSIONID=90D84EF5D5BD1C4008F332F9EDA8F9AA; Path=/; Secure;
if (header.getValue().contains(";")){
finalCookie += String.format("%s; ", header.getValue().split(";")[0]);
} else {
finalCookie += String.format("%s; ", header.getValue());
}
}
//finalCookie = JSESSIONID=1B70CAB822430E14991E14ACAE153F5D;
SharedPreferences sharedPreferences = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("cookie", finalCookie);
editor.commit();
}
Now in your StoredPreferences you will have correctly formatted cookies (in your case you are only returning one from your server but it's likely that more than one Set-Cookie header can be included in a server response. So this implementation parses out each cookie and builds a cookie string in the correct format.
Since we are now correctly building the cookie string ourselves you can remove the CookieStore and just pull the "cookie" value out of SharedPreferences and use the string that is returned in your setHeader call:
SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
String stringCookie = sharedPreferences.getString("cookie", "");
...
httpPost.setHeader("Cookie", cookie);
Keep in mind that although this implementation will work for you it won't take into consideration if cookies change throughout your session. Since you are the author of the web application you should be aware of whether or not cookies change - maybe when you log in it's the only time you will be given cookies to set in the browser and this solution will work for you. But a more robust solution will be to save out all of the cookies individually (instead of using editor.putString you can use editor.putStringSet to save out all of the cookie headers) then when you want to build a cookie for the respond you can .add() each individual cookie to the cookie store and then use the cookie store the same way you were before. This way each cookie by name is stored individually so that if you ever get another Set-Cookie header again for a cookie that you already have loaded in your client it will correctly overwrite that value with the updated cookie value.

Looping upload only uploads the first item

I have a for loop running in the background, and it's uploading an array to my database online. However, it only uploads the first time it goes through, and I can't figure out why.
I can follow the code as it loops through the params.add and the upload, but when I look at my database, only one extra item is added each time. My success int is also set to 0 each time but the first one.
I've looked at similar problems, and tried to fix this, but I can't find anything. I'd appreciate any help on this.
This is the relevant code ( I call it with "new SavePotholeDetails().execute();" ):
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(SensorActivity.this);
pDialog.setMessage("Loading pothole details. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
/**
* Saving product
* */
protected String doInBackground(String... args) {
// TODO: Get data from sensorData
for (int i = 0; i < sensorData.size(); i++) {
// Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(TAG_TIME, Long.toString(sensorData.get(i).getTimestamp())));
params.add(new BasicNameValuePair(TAG_ACCEL_X, Double.toString(sensorData.get(i).getX())));
params.add(new BasicNameValuePair(TAG_ACCEL_Y, Double.toString(sensorData.get(i).getY())));
params.add(new BasicNameValuePair(TAG_ACCEL_Z, Double.toString(sensorData.get(i).getZ())));
params.add(new BasicNameValuePair(TAG_CLIENT_ID, "Epidilius")); //TODO: Make a client ID variable
params.add(new BasicNameValuePair(TAG_GPS_X, Double.toString(sensorData.get(i).getLat())));
params.add(new BasicNameValuePair(TAG_GPS_Y, Double.toString(sensorData.get(i).getLng())));
// sending modified data through http request
// Notice that update product url accepts POST method
JSONObject json = jsonParser.makeHttpRequest(
url_update_pothole, "POST", params);
// check json success tag
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
// successfully updated
Intent intent = getIntent();
// send result code 100 to notify about product update
setResult(100, intent);
finish();
} else {
// failed to update product
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return null;
}
protected void onPostExecute(String file_url) {
// dismiss the dialog once product updated
pDialog.dismiss();
}
EDIT:
Here is the JSONParser class:
public class JSONParser {
static InputStream is = null;
static JSONObject jObj = null;
static String json = "";
// constructor
public JSONParser() {
}
// function get json from url
// by making HTTP POST or GET mehtod
public JSONObject makeHttpRequest(String url, String method,
List<NameValuePair> params) {
// Making HTTP request
try {
// check for request method
if(method == "POST"){
// request method is POST
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}else if(method == "GET"){
// request method is GET
DefaultHttpClient httpClient = new DefaultHttpClient();
String paramString = URLEncodedUtils.format(params, "utf-8");
url += "?" + paramString;
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
is, "UTF-8"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
json = sb.toString();
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
// try parse the string to a JSON object
try {
jObj = new JSONObject(json);
} catch (JSONException e) {
Log.e("JSON Parser", "Error parsing data " + e.toString());
}
// return JSON String
return jObj;
}
}
If you're just running a simple loop and not blocking the loop while your AsyncTasks are executing, then they're just getting fired all at once. I also see that you're sending your web requests through the jsonParser object, but I don't know what that is. If this object is shared between AsyncRequest instances (for example, if all of this stuff is scoped within a single Activity) or if the implementation blocks so that only one request can go out at a time, then everything after the first iteration of the loop is going to fail because your HTTP client is busy.
Without posting more than your AsyncTask code, I can't help much further than that. You might want to think more about how you're executing your loop. You could do something where the loop waits for each AsyncTask instance to call back (from the onPostExecute method) to signal that it's finished. Or, you can do the looping inside of a single AsyncTask instance (probably a more lightweight solution as only a single thread is created).

HTTPResponse's string from Play is empty

I am using Play and Faye on my Server. Play is used for API calls, while Faye is used for communication with the clients.
So, I have this method in the server:
public static Result broadcast(String channel, String message)
{
try
{
FayeClient faye = new FayeClient("localhost");
int code = faye.send(channel, message);
// print the code (prints 200).
return ok("Hello"); <------------ This is what we care about.
}
catch(Exception e)
{
return ok("false");
}
}
this is the code on the client, which is an android phone.
(it's the HTTP post method, which sends something to the server and gets a response back
The problem is, I can't print the message of the response.
public static String post(String url, List<BasicNameValuePair> params)
{
HttpClient httpclient = new DefaultHttpClient();
String result = "";
// Prepare a request object
HttpPost httpPost;
httpPost = new HttpPost(url);
httpPost.setHeader("Content-type", "application/json");
httpPost.setHeader("Accept", "application/json");
JSONObject obj = new JSONObject();
try
{
for (NameValuePair pair : params)
obj.put(pair.getName(), pair.getValue());
}
catch (JSONException e)
{
return e.getMessage();
}
// Add your data
try
{
httpPost.setEntity(new StringEntity(obj.toString(), "UTF-8"));
}
catch (UnsupportedEncodingException e)
{
return e.getMessage();
}
HttpResponse httpResponse;
try
{
httpResponse = httpclient.execute(httpPost);
// Get hold of the response entity
HttpEntity entity = httpResponse.getEntity();
String str = EntityUtils.toString(entity);
Log.e("RestClient", "result = \"" + str + "\""); // hello should be printed here??
}
catch(Exception e)
{
// ...
}
The problem is that in logcat, what is printed is [result = ""]. Am I doing something wrong?
Thank you.
Use a tool such as Fiddler and see what the HTTP response contains.

Destroying or Closing HttpClient Android

I have a problem that I think it may relate to the HttpClient. I am logging into a website and grabbing data from it using JSoup. Everything works fine. Here is my issue, when I want to log into a different account, it displays the same data from the other account. Only when I kill the app is when I am able to login with different credentials. I think that my session with the website is still stored in the same HttpClient and won't let me log in again with a different account unless I logout. What would be the best way to fix this problem? Using HttpGet method to execute the logout script? Or is there a way to reset the HttpCLient. Thanks. Code:
public void parseDoc() {
new Thread(new Runnable() {
#Override
public void run() {
final HttpParams params = new BasicHttpParams();
HttpClientParams.setRedirecting(params, true);
httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(
"https://secure.groupfusion.net/processlogin.php");
String HTML = "";
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
3);
nameValuePairs
.add(new BasicNameValuePair(
"referral_page",
"/modules/gradebook/ui/gradebook.phtml?type=student_view&jli=t&jli=t&jli=t&jli=t&jli=t&jli=t&printable=FALSE&portrait_or_landscape=portrait"));
nameValuePairs.add(new BasicNameValuePair("currDomain",
"beardenhs.knoxschools.org"));
nameValuePairs.add(new BasicNameValuePair("username",
username.getText().toString()));
nameValuePairs.add(new BasicNameValuePair("password",
password.getText().toString()));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HTML = EntityUtils.toString(response.getEntity());
Document doc = Jsoup.parse(HTML);
Element link = doc.select("a").first();
String linkHref = link.attr("href");
HttpGet request = new HttpGet();
try {
request.setURI(new URI(linkHref));
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
response = httpclient.execute(request);
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder str = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
str.append(line);
}
in.close();
HTML = str.toString();
doc = Jsoup.parse(HTML);
Elements divs = doc.getElementsByTag("tbody");
for (Element d : divs) {
if (i == 2) {
finishGrades();
break;
}
i++;
ggg = d.html();
}
} catch (ClientProtocolException e) {
} catch (IOException e) {
}
}
}).start();
}
You can call httppost.abort();
I ran into a similar problem, although I am using one instance of the HttpClient for the whole application (Singleton). So in that case, if you use preemptive authentication, you can simply do this for the application-wide client instance (e.g. if the user logs out):
public void logOut() {
// keep in mind, 'httpClient' is final and set once in the constructor (Singleton)
httpClient.getCredentialsProvider().clear();
httpClient.getCookieStore().clear(); // important
}
See also: HttpClient State Management
I would suggest setting "no-cache" header of httpclient to false and try. Not guaranteed solution.

Categories

Resources