0

I'm working on a basic android app that makes a POST with HttpURLConnection. I want to return the response Message from my Web API.

My MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView mTextView = findViewById(R.id.textView);
        AsyncExample asyncExample = new AsyncExample();
        asyncExample.execute();
        mTextView.setText(asyncExample.getResponseMsg());
    }
}

My AsyncExample.java

class AsyncExample extends AsyncTask<Void, Void, Void> {
    private HttpURLConnection con;
    private String responseMsg;

    protected void onPreExecute() {
        responseMsg = "empty message";
    }

    @Override
    protected Void doInBackground(Void... params) {
        String urlParameters = "param1=data1";

        byte[] postData = urlParameters.getBytes(Charset.forName("UTF-8"));
        int postDataLength = postData.length;
        String request = "http://192.168.1.30:6262";
        URL url = null;
        try {
            url = new URL(request);
            con = (HttpURLConnection) url.openConnection();
            con.setDoOutput(true);
            con.setInstanceFollowRedirects(false);
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            con.setRequestProperty("charset", "utf-8");
            con.setRequestProperty("Content-Length", Integer.toString(postDataLength));
            responseMsg = con.getResponseMessage();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getResponseMsg() {
        return responseMsg;
    }
}

After running the app, i get empty message in my TextView. Why it is not getting updated my doInBackground? Even if con.getResponseMessage() is null, it should be updated?

BeGreen
  • 765
  • 1
  • 13
  • 39
  • you should return the response from doinbackground method and in onPostexecute store that string response and now you will be able to access that string response – Abdul Waheed Nov 30 '17 at 14:11
  • yet another ... [IT IS MULITHREADING](https://ideone.com/PPHi95) – Selvin Nov 30 '17 at 14:27

6 Answers6

2

Your doInBackground method take time to execute. you are immediately calling mTextView.setText(asyncExample.getResponseMsg()); but asynctask has been not been finished yet. You need to wait until your doInBackground finish and then call that setText you can do it in onPostExecute method.

Sunny
  • 14,522
  • 15
  • 84
  • 129
  • Ok I understand that my HTTP request is not finished when I try to access my string. What if I want to return a value in `OnPostExecute` instead of updating a TextView? – BeGreen Nov 30 '17 at 15:04
  • You can't return value from `onPostExecute`. One way I can think of this is using `CountDownLatch ` but it's overkill. Dealing with AsyncTask is a mess. RxJava solve this problem. – Sunny Dec 01 '17 at 05:30
2

The problem is that your AsyncTask is executed asynchronously, while you try to retrieve the value immediately. You need to implement this a little bit differently. Either leverage the API of AsyncTask, sine it posts callbacks for your on the UI thread. You can update your TextView directly in your AsyncTask

class MyAwesomeAsyncTask extends AsyncTask<Void, Void, String> {

    @Override
    protected void onPreExecute() {
        myTextView.setText("progress started");
    }

    @Override
    protected String doInBackground(final Void... voids) {
        String s = amazingCallToBackend();
        return s;
    }

    @Override
    protected void onPostExecute(final String s) {
        myTextView.setText(s);
    }
}

Or if you just want the value, you can pass a Callback to your async task that will deliver the value to you, something like that

interface Callback {
    void onValueReceived(String value);
    void onFailure();
}

class MyAwesomeAsyncTask extends AsyncTask<Void, Void, String> {

    private Callback callback;

    MyAwesomeAsyncTask(final Callback callback) {
        this.callback = callback;
    }

    @Override
    protected String doInBackground(final Void... voids) {
        String s = amazingCallToBackend();
        return s;
    }

    @Override
    protected void onPostExecute(final String s) {
        callback.onValueReceived(s);
    }
}

Here's how you create it

Callback callback = new Callback() {
        @Override
        public void onValueReceived(final String value) {

        }

        @Override
        public void onFailure() {

        }
    };

new MyAwesomeAsyncTask(callback).execute();

However, be careful because if for some reason your activity/fragment is gone/finished before your AsyncTask is done, this can cause memory leaks.

A quick Google search will tell you all you want about AsyncTask leaking memory :)

elmorabea
  • 3,243
  • 1
  • 14
  • 20
1

AsyncTask has 3 default method 1. On preexecute 2. Do in background 3. On post execute

post execute:

The response got from the doinbackground is in the post execute. Here we can process the result . use the runnable method to update text view ui

1

Override the onPostExecute() Method to return the text. In the Main Activity create a method to update the TextView after completing the execution of the Async Task. It is coming blank as because the Main Thread is not paused its executing and setting the text View but the Async task has not yet finished executing and hence the String is empty. So wait for the Async Task to finish before setting the text view.

String str_result= new RunInBackGround().execute().get();

Refer to this for more information.

Pronoy999
  • 645
  • 6
  • 25
  • So by default, the `get` method will find the return value of `onPostExecute`? I can't manage to make `onPostExecute` return a `String`, it says I can't override the method – BeGreen Nov 30 '17 at 15:00
  • You wont be able to return the String from the PostExecute of course, you can make the Async Task class a Inner class of the class where you have the MainActivity and then invoke a method of the outer class to update the textView from the Inner Aysnc Task class. If you need help you can refer to this code. https://github.com/Pronoy999/NewsApp/blob/master/app/src/main/java/com/news/mukhe/newsapp/NewsActivity.java – Pronoy999 Nov 30 '17 at 15:11
0

Try to do like this

MainActivity

public class MainActivity extends AppCompatActivity {
TextView mTextView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTextView = findViewById(R.id.textView);
    AsyncExample asyncExample = new AsyncExample(this,mTextView);
    asyncExample.execute();
  }
}

AsyncTask

    class AsyncExample extends AsyncTask<Void, Void, Void> {
    private HttpURLConnection con;
    private String responseMsg;
    private MainActivity mContext;
    TextView mTextView;

    public AsyncExample (MainActivity context, TextView textView) {
        mContext = context;
        mTextView = textView;
    } 

    protected void onPreExecute() {
        responseMsg = "empty message";
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    protected Void doInBackground(Void... params) {
        String urlParameters = "param1=data1";

        byte[] postData = urlParameters.getBytes(StandardCharsets.UTF_8);
        int postDataLength = postData.length;
        String request = "http://192.168.1.30:6262";
        URL url = null;
        try {
            url = new URL(request);
            con = (HttpURLConnection) url.openConnection();
            con.setDoOutput(true);
            con.setInstanceFollowRedirects(false);
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            con.setRequestProperty("charset", "utf-8");
            con.setRequestProperty("Content-Length", Integer.toString(postDataLength));
            responseMsg = con.getResponseMessage();
            mContext.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mTextView.setText(responseMsg);
                }
            });
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getResponseMsg() {
        return responseMsg;
    }
}
Manish Singh Rana
  • 822
  • 1
  • 13
  • 26
0

You can do it in a number of ways. I'd try to suggest you a way that would require negligible amount of changes to your existing code.

Declare the mTextView as a Global variable, Override onPostExecute() method inside your AsyncExample and update mTextView inside that onPostExecute() method with the value passed to it by the doInBackground() method [here, notice that responseMsg is returned at the end of doInBackground() ground which is caught as a String value (result) by the onPostExecute() method]. However, I also think that its a good idea to Override your onPreExecute() method.

In order to do so, your MainActivity.java should be as follows:

public class MainActivity extends AppCompatActivity {
TextView mTextView;    //declare mTextView outside the onCreate() method as a Global String variable.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.textView);
        AsyncExample asyncExample = new AsyncExample();
        asyncExample.execute();
    }
}

Please make your asynctask an inner-class of the same activity and edit it as follows:

    class AsyncExample extends AsyncTask<Void, Void, Void> {
    private HttpURLConnection con;
    private String responseMsg;

    @Override           // Its a good practice to Override the onPreExecute() method.
    protected void onPreExecute() {
        responseMsg = "empty message";
    }

    @Override
    protected String doInBackground(String... params) {
        String urlParameters = "param1=data1";

        byte[] postData = urlParameters.getBytes(Charset.forName("UTF-8"));
        int postDataLength = postData.length;
        String request = "http://192.168.1.30:6262";
        URL url = null;
        try {
            url = new URL(request);
            con = (HttpURLConnection) url.openConnection();
            con.setDoOutput(true);
            con.setInstanceFollowRedirects(false);
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            con.setRequestProperty("charset", "utf-8");
            con.setRequestProperty("Content-Length", Integer.toString(postDataLength));
            responseMsg = con.getResponseMessage();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return responseMsg;    //return the value of responseMsg
    }

        @Override      //override onPostExecute() method
        protected void onPostExecute(String result) {    //receive the value to be set to mTextView which is returned by the doInBackground() method.
           mTextView.setText(result);
    }

}
M. Arabi Hasan Sakib
  • 2,626
  • 2
  • 13
  • 20