I recently worked on an application which connects and parse data from a webserver into an android device, tested on froyo and gingerbread and works fine but crashes on ICS devices. As for the UI I used gingebread objects so I think it's not the issue.
It works just fine on the first run even after connecting to facebook but errors on the part where it fetch and parse all of the JSON data gathered.
Here's the logcat errors straight from the device:
E/AndroidRuntime(25620): FATAL EXCEPTION: Thread-9660
E/AndroidRuntime(25620): java.lang.UnsupportedOperationException
E/AndroidRuntime(25620): at java.lang.Thread.stop(Thread.java:1076)
E/AndroidRuntime(25620): at java.lang.Thread.stop(Thread.java:1063)
E/AndroidRuntime(25620): at com.android.guestlist.SplashScreen$1.run(Spla
shScreen.java:89)
E/android.os.Debug( 2090): !#Dumpstate > dumpstate -k -t -n -z -d -o /data/log/d
umpstate_app_error
E/Launcher(18546): Error finding setting, default accessibility to not found: ac
cessibility_enabled
E/log_tag (25620): Error in http connection android.os.NetworkOnMainThreadExcept
ion
E/log_tag (25620): Error converting result java.lang.NullPointerException
E/log_tag (25620): Error parsing data org.json.JSONException: End of input at ch
aracter 0 of
E/log_tag (25620): Error in http connection android.os.NetworkOnMainThreadExcept
ion
E/log_tag (25620): Error converting result java.lang.NullPointerException
E/log_tag (25620): Error in http connection android.os.NetworkOnMainThreadExcept
ion
E/log_tag (25620): Error parsing data org.json.JSONException: End of input at ch
aracter 0 of
And here's my JSON parser since I think this is where the problem happens? And oh, the logs I created doesn't even appear of the phone logs Well I can't really say but here is the code:
public void getDataFromWeb(String a, String b){
String result = "";
Log.v(TAG, "Name Value Pair a " + a);
Log.v(TAG, "Name Value Pair b " + b);
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("a_param",a));
nameValuePairs.add(new BasicNameValuePair("b_param",b));
try{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://[mywebserviceshere].php");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
Log.v(TAG, "connected");
}catch(Exception e){
Log.v(TAG, "run failed");
Log.e("log_tag", "Error in http connection "+e.toString());
}
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
sb = new StringBuilder();
String line = "0";
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
result=sb.toString();
Log.v(TAG, "buffered read");
}catch(Exception e){
Log.v(TAG, "buffered error");
Log.e("log_tag", "Error converting result "+e.toString());
}
try{
Log.v(TAG, result);
JSONArray jArray = new JSONArray(result);
JSONObject json_data=null;
for(int i=0;i<jArray.length();i++){
Log.v(TAG, "loop start");
json_data = jArray.getJSONObject(i);
w.add(json_data.getString("w_data"));
x.add(json_data.getString("x_data"));
y.add(json_data.getString("y_data"));
z.add(json_data.getString("y_data"));
Log.v(TAG, "list added");
}
}catch(JSONException e){
w.add("0");
x.add("No Data");
y.add("No Data");
z.add("No Data");
Log.v(TAG, "rest failed");
Log.e("log_tag", "Error parsing data "+e.toString());
}
}
And finally here's exactly what it looks like in AVD:
Is this an issue on ICS or I have to revise some of my codes?
You've got a NetworkOnMainThreadException and that is generally a sign of poor code, and it'll result in a bad user experience because the application locks up whenever it's running some sort of network activity. Yes, it (unfortunately) works on pre-Honeycomb devices, but it's not what anyone should be going for.
To solve your problem, I'd highly recommend you use a single AsyncTask and do all your HTTP calls in the doInBackground() method. Once they're completed, the onPostExecute() method will be called automatically, so you can update the GUI. Quite simple.
Take a look at the documentation for AsyncTask and understand it before doing anything else. I'm sure this will work perfectly for your application: http://developer.android.com/reference/android/os/AsyncTask.html
So no - it's not a problem with Ice Cream Sandwich. It simply won't allow you to make network requests on the main thread because it can block everything else.
(this is pretty similar to another question I answered a few days ago, so part of the answer is copied from there: https://stackoverflow.com/a/11897381/762442)
You are getting this exception because you are performing network operation in main thread
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://[mywebserviceshere].php");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
This was allowed before Honeycomb but after honeycomb this has been stopped and if you do so, you will get NetworkOnMainThreadException.
So its better to use AsyncTask. Using this, you can perform network operation in background thread(other than main thread).
To know more how AsyncTask works, see this link
Use the Asynctask in your code your getting NetworkOnMainThreadException ,
not only with ics but also with honeycomb it gives you the same Exception.
Related
I have this issue that has caused me to pound my head against the wall. I am writing a newspaper app that parses data in JSON from a database and displays it. The app works fine and passes data on WiFi and 4G, but chokes on 3G. Most of the time it takes between 30 seconds and 1 minute to grab data on 3G while only taking one to two seconds on WiFi. I often receive a warning message stating: HttpHostConnectException: Connection refused. I know the site works perfectly fine and is not causing issues because I can query fine on WiFi and 4G along with navigating from a desktop just fine with no problems. As another test, I borrowed my coworkers MiFi which is only on 3G in our area, and connected my device to it, and it passes data just fine although it is only 3G back to the Internet. So after looking at this, and trying to find a solution, I have come to the conclusion that maybe I am not doing something right on my end. To the best of my knowledge, everything is fine, but I am no expert. Any insight on this would be greatly appreciated.
Summary--
4G = Works
WiFI = Works
3G = Extremely slow
3G via WiFi(MiFi on 3G) =Works
public JSONObject makeHttpRequest(String url, String method, List params) {
// Making HTTP request
try {
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();
System.out.println("---GET--- Now grabing GET DATA");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
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();
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;
}
Is the 3G on my MiFi equally slow? Cause otherwise it sounds like you are saying that your process fails where the connection is slow.
You mention that 3G takes > 30s. Are you running on app engine? GAE has a hard limit on how long transactions can take - I believe that limit is 30s.
What if you added a delay on your server so that even a Wifi request takes as long as 3G tests are taking now - to verify that it is the time taken that is causing the failure.
Also, I think those 3G results sound rather poor. I don't know how much data you are retrieving but it really doesn't sound like it should take that long. So perhaps your 3G connection is simply a poor quality connection (and the MiFi perhaps is a better 3G connection).
I've been struggling a bit on sending JSON objects from an application on android to a php file (hosted locally). The php bit is irrelevant to my issue as wireshark isn't recording any activity from my application (emulation in eclipse/ADK) and it's almost certainly to do with my method of sending:
try {
JSONObject json = new JSONObject();
json.put("id", "5");
json.put("time", "3:00");
json.put("date", "03.04.12");
HttpParams httpParams = new BasicHttpParams();
HttpClient client = new DefaultHttpClient(httpParams);
//
//String url = "http://10.0.2.2:8080/sample1/webservice2.php?" +
// "json={\"UserName\":1,\"FullName\":2}";
String url = "http://localhost/datarecieve.php";
HttpPost request = new HttpPost(url);
request.setEntity(new ByteArrayEntity(json.toString().getBytes(
"UTF8")));
request.setHeader("json", json.toString());
HttpResponse response = client.execute(request);
HttpEntity entity = response.getEntity();
// If the response does not enclose an entity, there is no need
if (entity != null) {
InputStream instream = entity.getContent();
}
} catch (Throwable t) {
Toast.makeText(this, "Request failed: " + t.toString(),
Toast.LENGTH_LONG).show();
}
I've modified this from an example I found, so I'm sure I've taken some perfectly good code and mangled it. I understand the requirement for multi-threading so my application doesn't hang and die, but am unsure about the implementation of it. Would using Asynctask fix this issue, or have I missed something else important?
Thankyou for any help you can provide.
Assuming that you are using emulator to test the code, localhost refers to the emulated environment. If you need to access the php hosted on your computer, you need to use the IP 10.0.2.2 or the LAN IP such as 192.168.1.3. Check Referring to localhost from the emulated environment
You can refer to Keeping Your App Responsive to learn about running your long running operations in an AsyncTask
you should use asynctask or thread, because in higher versions of android it doesn't allow long running task like network operations from ui thread.
here is the link for more description
I'm struggling a bit with checking/verifying the result of a http post operation. I'm checking a database for certain entries, and then converting the result to a JSONArray. However, if the result is null, I don't want to attempt the conversion. I'm catching the exception, so the program doesn't crash if the result is zero, but I'd like to avoid attempting to convert an empty result alltogether.
I'm connecting to the database (via a php script) like this:
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
StringBuilder authentication = new
StringBuilder().append("user").append(":").append("pass");
result = Base64.encodeBytes(authentication.toString().getBytes());
httppost.setHeader("Authorization", "Basic " + result);
nameValuePairs.add(new BasicNameValuePair("date", date));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
} catch (Exception e) {
Log.e(DEBUG_TAG, "Error in http connection" + e.toString());
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-
8859-1"),8);
sb = new StringBuilder();
sb.append(reader.readLine() + "\n");
is.close();
result = sb.toString();
} catch (Exception e) {
Log.e("log_tag", "Error converting result "+e.toString());
}
As you can see, I'm getting the result from the server and converting it to a string.
Then I wan to convert this string into a JSONArray IF it's not empty. If it's empty, I want to skip it.
However, I cannot figure out how to catch if the converted result string if null or not.
If I do this, after the above code,
itemsview.append("Result: " + result)
then the appended text is "null". Which makes sense, since the returned result from the db (in this test) is blank.
However, if I do this to catch the null result,
if (result.equals("null") {
itemsview.append("Result is null!");
}
the text doesn't appear. So, if I print out the value of the result string, it is null. But when I check it for the value null in the if, then it doesn't turn out to be true.
What am I missing here?
BTW: What I'm doing after this code is, as mentioned, converting the result string to JSONArray, then inserting the values into a List
instead of
if (result.equals("null") {
itemsview.append("Result is null!");}
use
if (result==null) {
itemsview.append("Result is null!");}
I think better you check for result==null rather than what you are doing :) cheers :)
I'm writing an app to use an Android device to get and set data from an Access database on a local area network. I'm using HttpPost and/or HttpGet to communicate with php pages on the server which in turn communicate with the db via odbc.
It works well, but takes almost a second for each query to complete. Is there a faster way?
I'm debugging via usb on the actual device. When I access the php pages from the browser on the device, it's much faster. There is no other traffic on this network.
Thanks!
Edit: Here's the code to retrieve a dataset:
private JSONArray getData(String phpFile,ArrayList<NameValuePair> nameValuePairs){
String result = "";
InputStream is = null;
JSONArray jArray=null;
try{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://" + mServerIP + "/dressageNet/"+ phpFile);
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
}catch(Exception e){
Log.e("log_tag", "Error in http connection "+e.toString());
}
try{
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){
Log.e("log_tag", "Error converting result "+e.toString());
}
try{
jArray = new JSONArray(result);
}catch(JSONException e){
Log.e("log_tag", "Error parsing data "+e.toString());
}
nameValuePairs.clear();
return jArray;
}
Create custom server application in whatever you language want and stop using http server and PHP. PHP is slow and this creates overhead. of course you will need to implement your own protocol but it will be a lot faster. I did something like this in java me, and performance was way better than doing POST/GET.
How would one go about sending data back to server, from an android application?
I've already tried using HttpPost and posted back to a RESTful WCF service, but I couldnt get that to work (I've already created a SO question about this, without finding the solution..) - No matter what I do I keep getting 405 Method not allowed or the 400 Bad Request.. :(
I'm not asking for full code example necessarily.. just a pointer in a direction, which can enable me to send data back to a server.
It is important that the user should not have to allow or dismiss the transfer.. it should happen under the covers, so to speak
Thanks in advance
Services is the way to go. REST (I recommend this one on Android), or SOAP based. There're loads of tutorials on getting an android app communicate a service, even with .net / wcf ones.
Tho you can always just open raw sockets and send data with some custom protocol.
Edit:
Here's the doInBackground part of my asynctask handling http post communication, maybe that'll help:
protected String doInBackground(String... req) {
Log.d(TAG, "Message to send: "+req[0]);
HttpPost p = new HttpPost(url);
try{
p.setEntity(new StringEntity(req[0], "UTF8"));
}catch(Exception e){
e.printStackTrace();
}
p.setHeader("Content-type", "application/json");
String response = "";
try{
HttpResponse resp = hc.execute(p, localContext);
InputStream is = resp.getEntity().getContent();
response = convertStreamToString(is);
Log.d("Response", "Response is " + response);
} catch (Exception e){
e.printStackTrace();
}
return response;
}