Skip to content

Commit 9d02adb

Browse files
Merge pull request #270 from MihaiCristianCondrea/codex/fix-classcastexception-crash
Handle Retrofit todo response safely
2 parents 7f50c63 + c9be04b commit 9d02adb

File tree

2 files changed

+81
-23
lines changed

2 files changed

+81
-23
lines changed

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitActivity.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
import android.os.Handler;
66
import android.os.Looper;
77

8+
import androidx.annotation.Keep;
89
import androidx.annotation.NonNull;
910
import androidx.annotation.Nullable;
1011

1112
import com.d4rk.androidtutorials.java.R;
1213
import com.d4rk.androidtutorials.java.databinding.ActivityRetrofitBinding;
1314
import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity;
1415
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
16+
import com.google.gson.annotations.SerializedName;
17+
18+
import java.util.Map;
1519

1620
import retrofit2.Call;
1721
import retrofit2.Callback;
@@ -47,17 +51,17 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
4751
api.getTodo().enqueue(new Callback<>() {
4852
@Override
4953
public void onResponse(@NonNull Call<Todo> call, @NonNull Response<Todo> response) {
50-
if (response.isSuccessful() && response.body() != null) {
51-
binding.textViewResult.setText(response.body().title);
54+
if (response.isSuccessful()) {
55+
displayTodoTitle(response);
5256
} else {
53-
binding.textViewResult.setText(R.string.snack_general_error);
57+
showGeneralErrorMessage();
5458
}
5559
binding.buttonFetch.setEnabled(true);
5660
}
5761

5862
@Override
5963
public void onFailure(@NonNull Call<Todo> call, @NonNull Throwable t) {
60-
binding.textViewResult.setText(R.string.snack_general_error);
64+
showGeneralErrorMessage();
6165
binding.buttonFetch.setEnabled(true);
6266
}
6367
});
@@ -74,12 +78,37 @@ protected void onDestroy() {
7478
handler.removeCallbacksAndMessages(null);
7579
}
7680

81+
private void displayTodoTitle(@NonNull Response<Todo> response) {
82+
Object body = response.body();
83+
if (body instanceof Todo) {
84+
Todo todo = (Todo) body;
85+
if (todo.title != null && !todo.title.isEmpty()) {
86+
binding.textViewResult.setText(todo.title);
87+
return;
88+
}
89+
} else if (body instanceof Map<?, ?> map) {
90+
Object title = map.get("title");
91+
if (title != null) {
92+
binding.textViewResult.setText(String.valueOf(title));
93+
return;
94+
}
95+
}
96+
showGeneralErrorMessage();
97+
}
98+
99+
private void showGeneralErrorMessage() {
100+
binding.textViewResult.setText(R.string.snack_general_error);
101+
}
102+
77103
interface JsonPlaceholderApi {
78104
@GET("todos/1")
79105
Call<Todo> getTodo();
80106
}
81107

82-
static class Todo {
108+
@Keep
109+
public static final class Todo {
110+
@SerializedName("title")
111+
@Nullable
83112
public String title;
84113
}
85114
}

app/src/main/res/raw/text_retrofit_java.txt

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ import android.os.Bundle;
55
import android.os.Handler;
66
import android.os.Looper;
77

8+
import androidx.annotation.Keep;
9+
import androidx.annotation.NonNull;
810
import androidx.annotation.Nullable;
911

12+
import com.d4rk.androidtutorials.java.R;
1013
import com.d4rk.androidtutorials.java.databinding.ActivityRetrofitBinding;
1114
import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity;
1215
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
16+
import com.google.gson.annotations.SerializedName;
1317

14-
import com.d4rk.androidtutorials.java.R;
18+
import java.util.Map;
1519

1620
import retrofit2.Call;
1721
import retrofit2.Callback;
@@ -21,19 +25,10 @@ import retrofit2.converter.gson.GsonConverterFactory;
2125
import retrofit2.http.GET;
2226

2327
public class RetrofitActivity extends UpNavigationActivity {
24-
private ActivityRetrofitBinding binding;
2528
private final Handler handler = new Handler(Looper.getMainLooper());
29+
private ActivityRetrofitBinding binding;
2630
private JsonPlaceholderApi api;
2731

28-
interface JsonPlaceholderApi {
29-
@GET("todos/1")
30-
Call<Todo> getTodo();
31-
}
32-
33-
static class Todo {
34-
public String title;
35-
}
36-
3732
@Override
3833
protected void onCreate(@Nullable Bundle savedInstanceState) {
3934
super.onCreate(savedInstanceState);
@@ -50,20 +45,20 @@ public class RetrofitActivity extends UpNavigationActivity {
5045

5146
binding.buttonFetch.setOnClickListener(v -> {
5247
binding.buttonFetch.setEnabled(false);
53-
api.getTodo().enqueue(new Callback<Todo>() {
48+
api.getTodo().enqueue(new Callback<>() {
5449
@Override
55-
public void onResponse(Call<Todo> call, Response<Todo> response) {
56-
if (response.isSuccessful() && response.body() != null) {
57-
binding.textViewResult.setText(response.body().title);
50+
public void onResponse(@NonNull Call<Todo> call, @NonNull Response<Todo> response) {
51+
if (response.isSuccessful()) {
52+
displayTodoTitle(response);
5853
} else {
59-
binding.textViewResult.setText(R.string.snack_general_error);
54+
showGeneralErrorMessage();
6055
}
6156
binding.buttonFetch.setEnabled(true);
6257
}
6358

6459
@Override
65-
public void onFailure(Call<Todo> call, Throwable t) {
66-
binding.textViewResult.setText(R.string.snack_general_error);
60+
public void onFailure(@NonNull Call<Todo> call, @NonNull Throwable t) {
61+
showGeneralErrorMessage();
6762
binding.buttonFetch.setEnabled(true);
6863
}
6964
});
@@ -79,4 +74,38 @@ public class RetrofitActivity extends UpNavigationActivity {
7974
super.onDestroy();
8075
handler.removeCallbacksAndMessages(null);
8176
}
77+
78+
private void displayTodoTitle(@NonNull Response<Todo> response) {
79+
Object body = response.body();
80+
if (body instanceof Todo) {
81+
Todo todo = (Todo) body;
82+
if (todo.title != null && !todo.title.isEmpty()) {
83+
binding.textViewResult.setText(todo.title);
84+
return;
85+
}
86+
} else if (body instanceof Map<?, ?> map) {
87+
Object title = map.get("title");
88+
if (title != null) {
89+
binding.textViewResult.setText(String.valueOf(title));
90+
return;
91+
}
92+
}
93+
showGeneralErrorMessage();
94+
}
95+
96+
private void showGeneralErrorMessage() {
97+
binding.textViewResult.setText(R.string.snack_general_error);
98+
}
99+
100+
interface JsonPlaceholderApi {
101+
@GET("todos/1")
102+
Call<Todo> getTodo();
103+
}
104+
105+
@Keep
106+
public static final class Todo {
107+
@SerializedName("title")
108+
@Nullable
109+
public String title;
110+
}
82111
}

0 commit comments

Comments
 (0)