4

I know there are lots of tutorials for OkHttp, but basically all of them do something different in the onResponse method and most don't bother to explain why. Some check for if (response.isSuccessful), some surround it with try/catch, some don't do any of this at all.

This is my example project. What is the proper way to handle the onResponse method?

public class MainActivity extends AppCompatActivity {

private TextView textViewResult;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textViewResult = findViewById(R.id.text_view_result);

    OkHttpClient client = new OkHttpClient();

    String url = "https://reqres.in/api/users?page=2";

    Request request = new Request.Builder()
            .url(url)
            .build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            final String myResponse = response.body().string();

            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    textViewResult.setText(myResponse);
                }
            });
        }
    });
}

}

  • Buddy your question is not clear the code you provided looks good. Are there any errors in that? Moreover it depends on your conditions that how you like to fetch response by if condition or try/catch. Mostly try block is used when you are serializing your response as xml or json. Try is used to check if response is serialized into object from your json or not to avoid crashes. – Muhammad Saad Rafique Dec 21 '17 at 11:30

4 Answers4

3

Update

onResponse of okhttp runs on background thread. So, yes, it's necessary to do MainActivity.this.runOnUiThread(...).

Original answer

onResponse callback already runs on ui thread AFAIK. So, you don't actually need to do MainActivity.this.runOnUiThread(...).

And everyone's onResponse is different because everyone has different needs. Use try/catch if your operations in onResponse might give error and you don't want it to crash.

For some network requests you may need to check if response is successful for other you may not. It all depends on use cases. Do what works for you best.

I'd suggest you surround your code in onResponse in a try/catch block because the user might close the app before the network request is finished. And when you set the textview text in onResponse it will crash because the activity and that textview doesn't exist anymore.

denvercoder9
  • 2,979
  • 3
  • 28
  • 41
  • Thanks for the explanation. runOnUiThread is necessary tho, otherwise it wont work. Can you answer me a noob question: I am confused by the terminlogoy. Is what I do in my example an "asynchronous network request"? A "network request" is the same as a "network call" i guess? – Franz Wildner Dec 21 '17 at 12:38
  • 1
    @FranzWildner A network request is a network call, yes. What "asynchronous" means that you make that network call in background, in other words, a separate thread not running or blocking your UI thread. – denvercoder9 Dec 21 '17 at 12:46
2

Adding to the answer from rafid. There are basically three cases you want to check.

  1. response.isSuccessful() => status code between 200 and 300
  2. response.code() => to manually check after response is not successful
  3. onFailure() => Network error or parsing error etc.

Ideally your callback would handle those cases something like

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // network error or parsing error
    }
    @Override
    public void onResponse(Call call, Response response) {
        if (response.isSuccessful()) {
            // do stuff all good
        } else {
            // handle different cases for different status codes or dump them all here
        }
    }
});

The reason you need a try-catch is because OkHttp is trying to parse the response. This is the case for example for response.errorBody().string();. Another case would be if your Callback<T> has actually a type parameter. Again OkHttp will try to parse the response to that type. If it fails it will result in a callback onto the onFailure method.

Murat Karagöz
  • 35,401
  • 16
  • 78
  • 107
  • Thanks for the detailed answer. My onResponse method throws an IOException. Now I am not really sure if I have to take care of that somewhere else or if I can just leave it like that. Do I have to catch this exception somewhere or is it ok to leave it like this? – Franz Wildner Dec 21 '17 at 12:09
  • @FranzWildner You will need to handle the catch because by calling `response.body().string()` it parses the response. It's the same as JSON parsing any string. It may throw an parsing exception. – Murat Karagöz Dec 21 '17 at 13:38
  • Ok, but what i don't understand: If i throw that exception on purpose (by writing `throw new IOException in onResponse`, it doesn't crash my app even if i don't catch it anywhere. Shouldn't my app crash if i don't catch that exception? – Franz Wildner Dec 21 '17 at 14:14
  • @FranzWildner If you don't handle the Exception it will crash the app. That's why you should use a try-catch there. – Murat Karagöz Dec 21 '17 at 14:34
  • If I throw it on purpose it does NOT crash, this is why I am wondering. Does that mean it gets catched somewhere else where I don't see it right now? – Franz Wildner Dec 21 '17 at 14:42
  • @FranzWildner In this case OkHttp is actually catching it see [here](https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/OkHttpCall.java#L137-L145) – Murat Karagöz Dec 21 '17 at 14:44
  • Thank you, this really clarifies it! – Franz Wildner Dec 21 '17 at 16:12
0

Edit: To be more clear.

Callback is running in mainThread so there is no need to call runOnUiThread. If response is not successful you can try to parse error body as below. If response is successful you can parse with Gson as i show.

String message = "";
            if (response.errorBody() != null) {
                try {
                    message = response.errorBody().string();
                } catch (IOException ignored) {
                    Log.e("OkHttp IOException", "error while parsing response");
                }
                 Log.d("Error Message", message);   
            }

I recommend you to use Gson Library. First you should create your pojo class. You can use http://www.jsonschema2pojo.org/ to create your pojo class. Then you can parse body like below

  Gson gson = new Gson();
  MyPojo myPojo = gson.fromJson(response.body().charStream(), MyPojo.class);
toffor
  • 1,219
  • 1
  • 12
  • 21
0

I think you need to make sure you know the legal response from the request, like an json or File. if it's just a json, use like below:

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String myResponse = response.body().string();
                if (response.isSuccessful() && !TextUtils.isEmpty(myResponse)) {
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            textViewResult.setText(myResponse);
                        }
                    });
                }
            }
nick zhou
  • 31
  • 6