Oh George
Oh George

Reputation: 63

Perform AsyncTask on Android / Posting JSON

I am working on an android app, and am running into some troubles with registering users. I want to post a JSON object to my server and receive one back. I can successfully create a JSON object with the right information but when I go to post it I get a NetworkOnMainThreadException or my HttpClient class returns null when it should be returning a JSONObject and I am very confident that my web server works correctly. I understand that you cannot connect to the network on the main thread and have created an HttpClient class that uses AsnycTask (although probably not correctly). I have been working on this for quite a while and would appreciate any guidance in the right direction.

//Main activity
@Override
public void onClick(View arg0) {
    if(!(isEmpty(name) || isEmpty(username) || isEmpty(password) || isEmpty(email))) {
        user = new JSONObject();
        try {
            user.put("username", username.getText().toString());
            user.put("name", name.getText().toString());
            user.put("email", email.getText().toString());
            user.put("password", password.getText().toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }

        jRegister = new JSONObject();

        try {
            jRegister.put("apiToken", Utilities.apiToken);
            jRegister.put("user", user);

            Log.i("MainActivity", jRegister.toString(2));

        } catch (JSONException e) {
            e.printStackTrace();
        }

       //
        HttpClient client = new HttpClient(url, jRegister);
        result = client.getJSONFromUrl();


        try {
            if(result  != null)
                tv.setText(result.toString(2));
            else
                 tv.setText("null");
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }else {
        tv.setText("");
    }
}

HttpClient Class

public class HttpClient extends AsyncTask<Void, Void, JSONObject>{
    private final String TAG = "HttpClient";
    private String URL;
    private JSONObject jsonObjSend;
    private JSONObject result = null;

    public HttpClient(String URL, JSONObject jsonObjSend) {
        this.URL = URL;
        this.jsonObjSend = jsonObjSend;
    }

    public JSONObject getJSONFromUrl() {
        this.execute();
        return result;
    }

    @Override
    protected JSONObject doInBackground(Void... params) {

            try {
                    DefaultHttpClient httpclient = new DefaultHttpClient();
                    HttpPost httpPostRequest = new HttpPost(URL);

                    StringEntity se;
                    se = new StringEntity(jsonObjSend.toString());

                    // Set HTTP parameters
                    httpPostRequest.setEntity(se);
                    httpPostRequest.setHeader("Accept", "application/json");
                    httpPostRequest.setHeader("Content-type", "application/json");

                    long t = System.currentTimeMillis();
                    HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);
                    Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");

                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                            // Read the content stream
                            InputStream instream = entity.getContent();

                            // convert content stream to a String
                            String resultString= convertStreamToString(instream);
                            instream.close();
                            resultString = resultString.substring(1,resultString.length()-1); // remove wrapping "[" and "]"

                            JSONObject jsonObjRecv = new JSONObject(resultString);

                            // Raw DEBUG output of our received JSON object:
                            Log.i(TAG,"<JSONObject>\n"+jsonObjRecv.toString()+"\n</JSONObject>");

                            return jsonObjRecv;
                    } 

            }
            catch (Exception e) {
                    e.printStackTrace();
            }
            return null;
    }

    protected void onPostExecute(JSONObject jObject) {
        result = jObject;
    }
    private static String convertStreamToString(InputStream is) {

            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();

            String line = null;
            try {
                    while ((line = reader.readLine()) != null) {
                            sb.append(line + "\n");
                    }
            } catch (IOException e) {
                    e.printStackTrace();
            } finally {
                    try {
                            is.close();
                    } catch (IOException e) {
                            e.printStackTrace();
                    }
            }
            return sb.toString();
    }

}

Upvotes: 2

Views: 9050

Answers (2)

frozenkoi
frozenkoi

Reputation: 3248

One problem in your code is that your expectations of AsyncTask aren't quite right. In particular this function:

public JSONObject getJSONFromUrl() {
    this.execute();
    return result;
}

AsyncTask runs the code in the doInBackground() function in a separate thread. This means that once you call execute() you have two parallel lines of execution. You end up with what's called a Race Condition. When you reach the return result line, a couple of things can be happening:

  • doInBackground() hasn't run and therefore result is still has the default value. In this case null.
  • doInBackground() can be in the middle of the code. In your particular case because it doesn't modify result then this doesn't affect you much. But it could be on any line (or middle of a line sometimes if operations aren't atomic) when that return happens.
  • doInBackground() could've finished, but since onPostExecute() runs on the UI thread it has to wait until your onClick handler is finished. By the time onPostExecute() has a chance to run onClick already tried to update tv with whatever it was that getJSONFromUrl returned, most likely null.

The way to set up tasks with AsyncTask is to give it the information it needs to do it's work, start it up with execute, and since you can't know how long it will take to complete, let it handle the finishing steps of the task.

This means that after calling execute you don't wait around for it's result to update views (like in your case), but rather rely on the AsyncTask's onPostExecute or related methods to take over the next steps.

For your case this would mean that your onPostExecute should look something like:

protected void onPostExecute(JSONObject result) {
    try {
        if(result  != null)
            tv.setText(result.toString(2));
        else
            tv.setText("null");
    } catch (JSONException e) {
        e.printStackTrace();
    }
}

Upvotes: 0

meda
meda

Reputation: 45500

I understand that you cannot connect to the network on the main thread and have created an HttpClient class that uses AsnycTask (although probably not correctly).

You are right you have not implemented it the right way.

In your onClick events (still on Main thread) you performed a network activity causing the error:

HttpClient client = new HttpClient(url, jRegister);
result = client.getJSONFromUrl();

Instead you should run the network operation inside of the AsnycTask

public class GetJsonTask extends AsyncTask<Void, Void, JSONObject >{
    private String URL;
    private JSONObject jsonObjSend;

    public GetJsonTask(String URL, JSONObject jsonObjSend) {
        this.URL = URL;
        this.jsonObjSend = jsonObjSend;
    }

    @Override
    protected JSONObject doInBackground(Void... params) {
    JSONObject jsonObjRecv;
           try {
                    DefaultHttpClient httpclient = new DefaultHttpClient();
                    HttpPost httpPostRequest = new HttpPost(URL);

                    StringEntity se;
                    se = new StringEntity(jsonObjSend.toString());

                    // Set HTTP parameters
                    httpPostRequest.setEntity(se);
                    httpPostRequest.setHeader("Accept", "application/json");
                    httpPostRequest.setHeader("Content-type", "application/json");

                    long t = System.currentTimeMillis();
                    HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);
                    Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");

                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                            // Read the content stream
                            InputStream instream = entity.getContent();

                            // convert content stream to a String
                            String resultString= convertStreamToString(instream);
                            instream.close();
                            resultString = resultString.substring(1,resultString.length()-1); // remove wrapping "[" and "]"

                            jsonObjRecv = new JSONObject(resultString);

                            // Raw DEBUG output of our received JSON object:
                            Log.i(TAG,"<JSONObject>\n"+jsonObjRecv.toString()+"\n</JSONObject>");


                    } 

            }
            catch (Exception e) {
                    e.printStackTrace();
            }
            return jsonObjRecv;         
    }

    protected void onPostExecute(JSONObject result) {
        try {
            if(result  != null)
                tv.setText(result.toString(2));
            else
                 tv.setText("null");
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }else {
        tv.setText("");
    }
    }

}

Then you call your async in onclik method like this:

public void onClick(View arg0) {
//.......
GetJsonTask client = new GetJsonTask(url, jRegister);
client.execute();
}

Upvotes: 1

Related Questions