Issue
I was dealing with multiple Retrofit API calls and processing of response data, which was taking more time than I had anticipated. So I added statements of the following sort after every major block of code:
Log.e(TAG, "CHECK {1}");
To my surprise most Log statements were printed instanteously, in a order different from the program flow. A function that depended on a different function for its parameter too was called before the latter's return object was being processed.
What exactly happened here? Is there any way to tweak this behaviour?
EDIT: Here is a gist of the code.
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
Log.e(TAG, "CHECK 1");
List<String> list = function1();
Log.e(TAG, "CHECK 6");
function2(list);
Log.e(TAG, "CHECK 10");
}
private List<String> function1(){
Log.e(TAG, "CHECK 2");
List<String> list = new ArrayList<>();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(urls.API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIClass feedAPI = retrofit.create(APIClass.class);
Call<APIResponse> call = feedAPI.list(parameter1, parameter2, parameter3);
Log.e(TAG, "CHECK 3");
call.enqueue(new Callback<APIResponse>() {
@Override
public void onResponse(Call<APIResponse> call, Response<APIResponse> response) {
try{
Log.e(TAG, "onResponse: Server Response: " + response.toString());
Log.e(TAG, "onResponse: Get Data: " + response.body().getData());
Log.e(TAG, "CHECK 4");
for (Object entry: response.body().getData()) {
Gson gson = new Gson();
Map<Object, Object> map = gson.fromJson(gson.toJson(entry), Map.class);
for (Object key: map.keySet())
{
if (key.toString().equals("xyz"))
list.add(map.get(key).toString());
}
}
}catch (NullPointerException e){
Log.e(TAG, "onResponse: NullPointerException: " +e.getMessage() );
}
}
@Override
public void onFailure(Call<APIResponse> call, Throwable t) {
Log.e(TAG, "onFailure: Unable to retrieve JSON: " + t.getMessage() );
Toast.makeText(getActivity(), "An Error Occurred", Toast.LENGTH_SHORT).show();
}
});
Log.e(TAG, "CHECK 5");
return list;
}
private void function2(List<String> userlist){
Log.e(TAG, "CHECK 7");
//api callback and processing similar to function1, including a "CHECK 8"
Log.e(TAG, "CHECK 9");
}
Logcat Output.
CHECK 1
CHECK 2
CHECK 3
CHECK 5
CHECK 6
CHECK 7
CHECK 9
CHECK 10
onResponse: Server Response: Response{...}
onResponse: Get Data: [...]
Solution
The solution to this is to create a new Thread (or a Service) that runs in parallel to the main UI Thread; and then make use of synchronous API calls in the newly created Thread, as demonstrated below.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(urls.API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIClass feedAPI = retrofit.create(APIClass.class);
Call<APIResponse> call = feedAPI.list(parameter1, parameter2, parameter3);
try {
Response<APIResponse> response = call.execute();
Log.e(TAG, "onResponse: Server Response: " + response.toString());
Log.e(TAG, "onResponse: Get Data: " + response.body().getData());
for (Object entry: response.body().getData()) {
Gson gson = new Gson();
Map<Object, Object> map = gson.fromJson(gson.toJson(entry), Map.class);
for (Object key: map.keySet())
{
if (key.toString().equals("xyz"))
list.add(map.get(key).toString());
}
}
}
}
catch (IOException e) {
Log.e(TAG, "IOException: "+e);
}
To learn more about Thread, Service, etc. in Android, refer to this question: How can I fix 'android.os.NetworkOnMainThreadException'?
Answered By - ihsingh2
Answer Checked By - Marilyn (JavaFixing Volunteer)