Do POST request with Android DefaultHTTPClient cause freeze on execute() - android

I need post data to server.
I use this code:
HttpClient client = new DefaultHttpClient();
try {
HttpPost httppost = new HttpPost(serverUrl);
StringEntity se = new StringEntity(data);
httppost.setEntity(se);
httppost.setHeader("Accept", "application/json");
httppost.setHeader("Content-type", "application/json");
// Execute HTTP Post Request
HttpResponse response = client.execute(httppost);
int statusCode = response.getStatusLine().getStatusCode();
Log.i(TVProgram.TAG, "ErrorHandler post status code: " + statusCode);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (client != null) {
client.getConnectionManager().shutdown();
}
}
But problem is that Android freeze on execute() method, application is blocked out and after some time Android tell me that application doesn't respond.
I tried to debug into SDK classes and it freeze in AbstractSessionInputBuffer class on the line 103 which is
l = this.instream.read(this.buffer, off, len);
I also tried it run the request in separated thread, but the same problem.
I tested it on Android 2.1 (emulator) and Android 2.2 real mobile device.
I also tried to set HTTP proxy and use Fiddler to check HTTP communication data are received by server and server also send correct answer and HTTP code 200. All seems to be ok.
What is wrong please?
UPDATE: When I use AndroidHttpClient which is part of Android 2.2 SDK it works great. But it is not in earlier version of Android. So I include it's source code in my app for now. But AndroidHttpClient use DefaultHTTPClient internally, so problem will be in configuration of DefaultHttpClient.

I am using a POST HTTP request successfully. Here is my code. I removed pieces using handler to display messages etc. and the handler itself.
The POST string is like "&NAME=value#NAME2=value2"...
protected class ConnectingThread implements Runnable
{
Message msg;
private Handler mExtHandler;
private String mData;
private String mUrl;
/**
* #param h (Handler) - a handler for messages from this thread
* #param data (String) - data to be send in HTTP request's POST
* #param url (String) - URL to which to connect
*/
ConnectingThread(Handler h, String data, String url) {
mExtHandler = h;
mData = data;
mUrl = url;
}
public void run() {
try {
// TODO use the handler to display txt info about connection
URL url = new URL(mUrl);
URLConnection conn = url.openConnection();
conn.setConnectTimeout(CONN_TIMEOUT_MILLIS);
conn.setDoOutput(true);
BufferedOutputStream wr = new BufferedOutputStream(conn.getOutputStream());
wr.write(mData.getBytes());
wr.flush();
wr.close();
String sReturn = null;
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
int length = conn.getContentLength();
char[] buffer = new char[length];
int read = rd.read(buffer);
if(read == length)
sReturn = new String(buffer);
rd.close();
buffer = null;
// TODO use the handler to use the response
} catch (Exception e) {
//....
}
// TODO use the handler to display txt info about connection ERROR
}
}

Isn't client.execute(httppost); synchronous ?
You probably need to put this in a thread, else it will freeze the UI.

Yes it is being freezed just becoz you haven't implemented this as Asynchronous process. Because while it makes web request, your UI will wait for the response and then it will be updated once the response is received.
So this should be implemented as Asynchronous process, and user should be notified (with progress bar or progress dialog) that there is something happening.
Now, Instead of implementing Runnable class, in android its preferrable and recommended to use AsyncTask, its also known as Painless Threading.
Do you background tasks inside the doInBackground() method.
Do your display type of operations inside onPostExecute() method, like updating listview with fetched data, display values inside TextViews....etc.
Display ProgressBar or ProgressDialog inside the onPreExecute() method.

Use AndroidHttpClient helped me in this situation.
But now complete AndroidHttpClient and DefaultHttpClient are obsolete in current version of Android so it is not important now.

Related

how to use URL to post data with HttpClient in android

I have to send/post some data to .svc Web Service that basically connect to remote database. I'm using JSONStringer to send the data but every time response status is false. My data is not sent. How to use HttpPost in Android . Can someone help me how to solve this .
Here is my webservice code
String namespace = "http://103.24.4.60/xxxxx/MobileService.svc";
public void ActivityUpload( final String strCurrentDateTime, final String strTitle, final String replaceDescChar, final String editedHashTag)
{
new AsyncTask<String, Void, String>()
{
#Override
protected String doInBackground(String... arg0)
{
String line = "";
try
{
Log.e("ActionDate "," = "+ strCurrentDateTime);
Log.e("ActivityId"," = "+strActivityId);
Log.e("UserId"," = "+str_UserId);
Log.e("ObjectId"," = "+strVessId);
Log.e("Name"," = "+strTitle);
Log.e("Remark"," = "+replaceDescChar);
Log.e("Status"," = "+"PENDING");
Log.e("Type"," = "+strType);
Log.e("starflag"," = "+0);
Log.e("HashTag"," = "+editedHashTag);
Log.e("Authentication_Token"," = "+str_Authentication_Token);
// make web service connection
HttpPost request = new HttpPost(namespace + "/Upd_Post_Activity");
request.setHeader("Accept", "application/json");
request.setHeader("Content-type", "application/json");
// Build JSON string
JSONStringer TestApp = new JSONStringer().object()
.key("ActionDate").value(strCurrentDateTime)
.key("ActivityId").value(strActivityId)
.key("UserId").value(str_UserId)
.key("ObjectId").value(strVessId)
.key("Name").value(strTitle)
.key("Remark").value(replaceDescChar)
.key("Status").value("PENDING")
.key("Type").value(strType)
.key("starflag").value("0")
.key("HashTag").value(editedHashTag)
.key("Authentication_Token").value(str_Authentication_Token).endObject();
StringEntity entity = new StringEntity(TestApp.toString());
Log.d("****Parameter Input****", "Testing:" + TestApp);
request.setEntity(entity);
// Send request to WCF service
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(request);
Log.d("WebInvoke", "Saving: " + response.getStatusLine().toString());
// Get the status of web service
BufferedReader rd = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
// print status in log
while ((line = rd.readLine()) != null) {
Log.d("****Status Line***", "Webservice: " + line);
}
} catch (Exception e) {
e.printStackTrace();
}
return line;
}
}.execute();
}
Here is input Parameter.
****Parameter Input****﹕ Testing:{"ActionDate":"2016-01-21%2014:20:43%20PM","ActivityId":"120160119180421058","UserId":"125","ObjectId":"1","Name":"Title2","Remark":"Test%20two","Status":"PENDING","Type":"3","starflag":"0","HashTag":"990075","Authentication_Token":"6321D079-5B28-4F3F-AEE7-D59A1B9EFA59"}
Thanks in advanced.
realize android httpclients are in process of deprecation ( in favor of httpsurlconnection ) but, these httpclients are still used pretty widely. On gradle builds, regard the deprication, and with small dependency lib tweeks , httpclient may be used for some time still.
( still gonna use httpclient ? )
Put android aside for a min.
learn how to CURL with JSON body for tests that show you what you EXACT JSON in body and exact HEADERS you will need to get success http result to a post ... ref here
Once you have that you can then go about transferring your curl test's components over to android.httpclient.exec.POST using httpclient of your choice.
Set the same group of Headers you had over in curl tests in your android post. apache.httpclient sample
2.a. make sure that default list of headers from the clients 'request' constructor does NOT include by default some headers you DO NOT want... In order to assure of this ,you probably will need to turn on HEADER logging for your client.... java example logger . remove unnecessary headers included by the framework constructor of POST.
2.b android logger (WIRE, HEADERS) is diff from and may take some digging , depend on what client is in use.
with the same headers as curl tests, set the http.posts request.entity to either a string or a properly encoded array of bytes containing the same JSON body used in the curl tests.
3.A. depending on the JSON lib, create your message objects and then convert the objects to some friendly type for enclosure in an entity for the post ie use a 'writer' to convert objects to a serialized string with the JSON.
reqRole = new ObjectMapper().createObjectNode();
reqRole.put("__type", "Pointer");
reqRole.put("className", "_Role");
reqRole.put("objectId", roleId);
rootOb.put("requestedRole", reqRole);
rootOb.put("requestedBy",usersArr);
StringWriter writer = new StringWriter();
try {
new ObjectMapper().writeValue(writer, rootOb)
..
String http-post-str=writer.toString();
3.B. wrap the string with json in the POST request...
httpPost.setEntity(new StringEntityHC4(http-post-str));
exec the request and youll get the same results you got in curl because the headers are same or nearly same and the body is the same , encoded string of json. same input = same result

How to know when a response from the server has been received?

Ok, what I want is to ask to my server for an url, and load that url on a WebView, but a problem appears: when you run a http request you need to do it in a separate thread, and loadUrl() needs to be runned in the UI thread. So, how can I do to first make the request and then load the url?
The code isn't inside an Activity, so I can't call runOnUiThread(Runnable).
How can I do it?
EDIT: As Alécio suggested, it's better to not have too much code inside custom views, so what I want to do is to create a method that allows me to pass the url when the app has received the response from the server. The code that I'm using for the request is:
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://requesturl.com");
try {
HttpResponse response = client.execute(post);
InputStream content = response.getEntity().getContent();
String url = getStringFromInputStream(content);
//web.loadUrl(url);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
So, it's possible to know when a response has been received?
WebView is a UI component it must be in the context of an Activity or Fragment, so loadUrl() from the UI thread is OK.
If you want to make a server call, process the response before displaying something on the UI, then you might just use the conventional options to connect to a server URL using the HttpURLConnection. See the sample code below:
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();
}
}
In this example, you really need to execute it in a background Thread. Never from the UI Thread.

Android HttpPost request exception

Just as a demonstration the code will work, I am attempting to fetch some JSON data within my oncreate function. I know it should run on a different thread but I want to be sure the code successfully fetches my JSON before moving it into it's own thread.
The code is below:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
/***************************************************/
final String TAG = "PostFetcher";
final String SERVER_URL = "http://kylewbanks.com/rest/posts";
// final String TAG = "PostsActivity";
// List<Post> posts;
try {
//Create an HTTP client
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(SERVER_URL);
//Perform the request and check the status code
HttpResponse response = client.execute(post);
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
try {
//Read the server response and attempt to parse it as JSON
Reader reader = new InputStreamReader(content);
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("M/d/yy hh:mm a");
Gson gson = gsonBuilder.create();
List<JsonObject> posts = new ArrayList<JsonObject>();
Log.e(TAG, "Checking: " + posts);
// posts = Arrays.asList(gson.fromJson(reader, JsonObject[].class));
content.close();
} catch (Exception ex) {
Log.e(TAG, "Failed to parse JSON due to: " + ex);
}
} else {
Log.e(TAG, "Server responded with status code: " + statusLine.getStatusCode());
}
} catch(Exception ex) {
Log.e(TAG, "Failed to send HTTP POST request due to: " + ex);
}
}
When I run the code, I get the second to last exception message:
Server responded with status code: 500
Can anyone please tell me what I'm doing wrong?
You are sending a HttpPost request to (obviously) an website that uses RESTful styled API.
This means, it works with HTTP Verbs (GET, POST, PUT, DELETE).
If you want to read data and the read access never changes data, use GET.
If you want to update or replace data, user PUT or POST (put usually to replace, POST to change/add). However, JavaScript does (or did) only support GET and POST requests, so keep that in mind.
If you want to delete a resource or collection, use DELETE.
That being said: If you want to load data, use Get in your case HttpGet instead of HttpPost.
Also read more about RESTful web APIs.
Edit:
In fact, calling the given URL in Fiddler2 (as stated in the comment on the other answer) results a HTML website reporting the error:
You called this URL via POST, but the URL doesn't end in a slash and
you have APPEND_SLASH set. Django can't redirect to the slash URL
while maintaining POST data. Change your form to point to
kylewbanks.com/rest/posts/ (note the trailing slash), or set
APPEND_SLASH=False in your Django settings.
Its internal server error..check if there are any exceptions are getting thrown at server side.
It has nothing to do with your android code, the problem is at server.
You can use AsyncTask to run network/filesystem related operations.

Android HTTP Client freezes

I'm experiencing some odd behavior in my HTTP requests. I have some users that are saying that this call isn't ever coming back (the spinner marking it's asynchronous call never goes away). I have seen this happen before, but I attributed it to the emulator going through Charles Proxy. I haven't yet seen it on actual phone until now.
I'm not sure what would cause this to happen, which is why I'm posting it here. Here's the call, using Jackson to deserialize the result into a Value Object. The two spots I saw the emulator freeze are httpclient.execute(httpGet); and getObjectMapper().readValue(jp, SyncVO.class);.
While debugging, stepping over the offending statement caused the debugger to never gain control back of stepping. Meanwhile, I see the request go out AND come back from the server through Charles. It's just that the app doesn't seem to get the response and just sits there.
So, here's the code. Thanks for any help!
public SyncVO sync(String userId, long lastUpdate, boolean includeFetch) throws IOException {
SyncVO result = null;
String url = BASE_URL + "users/" + userId + "/sync" + "?" + "fetch=" + includeFetch;
if (lastUpdate > 0) {
url += "&updatedSince=" + lastUpdate;
}
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Accept", "application/json");
httpGet.setHeader("Accept-Encoding", "gzip");
httpGet.setHeader(AUTHORIZATION, BEARER + " " + mOAuthToken);
httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, USER_AGENT_STRING);
httpclient.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
HttpResponse response = httpclient.execute(httpGet);
if (isUnauthorized(response)) {
APPLICATION.needReauthentication();
return null;
}
if (response != null) {
InputStream stream = response.getEntity().getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
stream = new GZIPInputStream(stream);
}
InputStreamReader inReader = new InputStreamReader(stream, "UTF-8");
JsonParser jp = mJsonFactory.createJsonParser(inReader);
result = getObjectMapper().readValue(jp, SyncVO.class);
}
return result;
}
private ObjectMapper getObjectMapper() {
return (new ObjectMapper()
.configure(Feature.AUTO_DETECT_FIELDS, true)
.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true));
}
don't forget to consume entities content after each request.
HttpEntity entity = response.getEntity();
try {
if (entity != null)
entity.consumeContent();
} catch (IOException e) {
e.printStackTrace();
}
You should definitely use connection timeout and socket read and be prepared for the worst from the server. Network operations will never be 100% predictable and there is not much your client can do then so make sure you code optimally.
httpParameters = httpclient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 5000);
HttpConnectionParams.setSoTimeout(httpParameters, 10000);
You can also cancel a task with asyncTask.cancel(true);
The reason is because you have left stream open. As such, the response is left in limbo. This means your global variable httpClient is also left in limbo, and unable to get a new entity when it re-uses the client.
You should call close() after finishing with the stream.
stream.close();
Network calls take a while and will block the UI thread. Same with your jackson deserialization code. This stuff needs to be put on a separate thread. See AsyncTask for an easy way to do it.

How to unit test a class that uses HttpClient in Android using the built-in framework?

I've got a class:
public class WebReader implements IWebReader {
HttpClient client;
public WebReader() {
client = new DefaultHttpClient();
}
public WebReader(HttpClient httpClient) {
client = httpClient;
}
/**
* Reads the web resource at the specified path with the params given.
* #param path Path of the resource to be read.
* #param params Parameters needed to be transferred to the server using POST method.
* #param compression If it's needed to use compression. Default is <b>true</b>.
* #return <p>Returns the string got from the server. If there was an error downloading file,
* an empty string is returned, the information about the error is written to the log file.</p>
*/
public String readWebResource(String path, ArrayList<BasicNameValuePair> params, Boolean compression) {
HttpPost httpPost = new HttpPost(path);
String result = "";
if (compression)
httpPost.addHeader("Accept-Encoding", "gzip");
if (params.size() > 0){
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
try {
HttpResponse response = client.execute(httpPost);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
if (entity.getContentEncoding() != null
&& "gzip".equalsIgnoreCase(entity.getContentEncoding()
.getValue()))
result = uncompressInputStream(content);
else
result = convertStreamToString(content);
} else {
Log.e(MyApp.class.toString(), "Failed to download file");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private String uncompressInputStream(InputStream inputStream)
throws IOException {...}
private String convertStreamToString(InputStream is) {...}
}
I cannot find a way to test it using a standard framework. Especially, I need to simulate total internet lost from inside the test.
There are suggestions to manually turn the Internet in the emulator off while performing the test. But it seems to me as not quite a good solution, because the automatic tests should be... automatic.
I added a "client" field to the class trying to mock it from inside the test class. But implementation of the HttpClient interface seems quite complex.
The Robolectric framework allows the developers to test Http connection as far as I know. But I guess there is some way to write such a test without using so big additional framework.
So are there any short and straightforward ways of unit testing classes that use HttpClient? How did you solve this in your projects?
I added a "client" field to the class trying to mock it from inside the test class. But implementation of the HttpClient interface seems quite complex.
I am a little bit confuse about this statement. From the question title, you are asking about unit-testing httpClint, by mocking a FakeHttpClient may help you unit-testing other part of app except httpClient, but doesn't help anything for unit-testing httpClient. What you need is a FakeHttpLayer for unit-testing httpClient (no remote server, network requires, hence unit-testing).
HttpClient Dummy Test:
If you only need examine app behavior in the situation that internet is lost, then a classic Android Instrument Test is sufficient, you can programmatically turn the Internet in the emulator off while performing the test:
public void testWhenInternetOK() {
... ...
webReader.readWebResource();
// expect HTTP 200 response.
... ...
}
public void testWhenInternetLost() {
... ...
wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(false);
webReader.readWebResource();
// expect no HTTP response.
... ...
}
This requires the remote http server is completely setup and in a working state, and whenever you run your test class, a real http communication is made over network and hit on http server.
HttpClient Advanced Test:
If you want to test app behavior more precisely, for instance, you want to test a http call in you app to see if it is handle different http response properly. the Robolectric is the best choice. You can use FakeHttpLayer and mock the http request and response to whatever you like.
public void setup() {
String url = "http://...";
// First http request fired in test, mock a HTTP 200 response (ContentType: application/json)
HttpResponse response1 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 200, null);
BasicHttpEntity entity1 = new BasicHttpEntity();
entity1.setContentType("application/json");
response1.setEntity(entity1);
// Second http request fired in test, mock a HTTP 404 response (ContentType: text/html)
HttpResponse response2 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 404, null);
BasicHttpEntity entity2 = new BasicHttpEntity();
entity2.setContentType("text/html");
response2.setEntity(entity2);
List<HttpResponse> responses = new ArrayList<HttpResponse>();
responses.add(response1);
responses.add(response2);
Robolectric.addHttpResponseRule(new FakeHttpLayer.UriRequestMatcher("POST", url), responses);
}
public void testFoo() {
... ...
webReader.readWebResource(); // <- a call that perform a http post request to url.
// expect HTTP 200 response.
... ...
}
public void testBar() {
... ...
webReader.readWebResource(); // <- a call that perform a http post request to url.
// expect HTTP 404 response.
... ...
}
Some pros of using Robolectric are:
Purely JUnit test, no instrument test so don't need start emulator (or real device) to run the test, increase development speed.
Latest Robolectric support single line of code to enable/disable FakeHttpLayer, where you can set http request to be interpreted by FakeHttpLayer (no real http call over network), or set the http request bypass the FakeHttpLayer(perform real http call over network). Check out this SO question for more details.
If you check out the source of Robolectric, you can see it is quite complex to implement a FakeHtppLayer properly by yourself. I would recommend to use the existing test framework instead of implementing your own API.
Hope this helps.

Categories

Resources