Search code examples
javaandroidandroid-asynctasksettext

Android SetText in OkHttp Async callback causing crash


I have an android activity that executes an Asynchronous Okhttp call, when the activity is loaded ( called from the onStart method as getActiveRequests()).

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

Button btLogout;
UserLocalStore userLocalStore;
String username;
String userEmail;
String recentAppName;
String recentRequestTime;
String isExpired;
TelephonyManager telephonyManager;
OkHttpClient client;
TextView tvRecentAppName, tvRecentRequestTime, tvIsExpired;


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

    userLocalStore = new UserLocalStore(this);
    telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

    TextView tvUserName = (TextView) findViewById(R.id.userName);
    TextView tvUserEmail = (TextView) findViewById(R.id.userEmail);

    tvRecentAppName = (TextView) findViewById(R.id.recentAppName);
    tvRecentRequestTime = (TextView) findViewById(R.id.recentRequestTime);
    tvIsExpired = (TextView) findViewById(R.id.isExpired);

    client = new OkHttpClient();
    username = userLocalStore.getLoggedInUser().name;
    userEmail = userLocalStore.getLoggedInUser().email;

    tvUserEmail.setText(userEmail);
    tvUserName.setText(username);

    btLogout = (Button) findViewById(R.id.btLogout);
    btLogout.setOnClickListener(this);
}

@Override
protected void onStart() {
    super.onStart();

    if(authenticateUser() == true){
        getActiveRequests();
    }else{
        startActivity(new Intent(this, LoginActivity.class));
    }
}

What I want to do Is update the UI once the Http call has been made using the SetText methods. Here is my call, implemented in the GetActiveRequests() method.

client.newCall(request)
            .enqueue(new Callback() {

                @Override
                public void onFailure(Call call, IOException e) {
                    String hello = "failed call";
                }

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

                    if (responseData != null) {
                        Gson gson = new Gson();
                        JsonElement element = gson.fromJson(responseData, JsonElement.class);
                        JsonObject jsonObject = element.getAsJsonObject();
                        final AccessRequest request = gson.fromJson(jsonObject, AccessRequest.class);

                        recentAppName = request.AppName.toString();
                        recentRequestTime = request.RequestTime.toString();
                        if (request.IsExpired)
                            isExpired = "Has Expired";
                        else isExpired = "Active";

                        tvRecentAppName.setText(recentAppName);
                        tvRecentRequestTime.setText(recentRequestTime);
                        tvIsExpired.setText(isExpired);
                    }
                }
            });

The problem I am having is that when the debugger reaches the SetText lines of code, it is causing the app to crash and close. I am at a loss as to how I can solve this but I assume it has something to with the Okhttp Async call not being able to update the UI, as my setText methods are working fine in onCreate().


Solution

  • That's because views' updates can only be done on UI thread and OkHttp's onResponse runs on background thread. Try to run that on main thread like this:

    @Override
    public void onResponse(Call call, Response response) throws IOException {
         final String responseData = response.body().string();
    
         if (responseData != null) {
            Gson gson = new Gson();
            JsonElement element = gson.fromJson(responseData, JsonElement.class);
            JsonObject jsonObject = element.getAsJsonObject();
            final AccessRequest request = gson.fromJson(jsonObject, AccessRequest.class);
    
            recentAppName = request.AppName.toString();
            recentRequestTime = request.RequestTime.toString();
            if (request.IsExpired)
                isExpired = "Has Expired";
            else isExpired = "Active";
    
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                   //Handle UI here                        
                   tvRecentAppName.setText(recentAppName);
                   tvRecentRequestTime.setText(recentRequestTime);
                   tvIsExpired.setText(isExpired);                
                }
           });
        }
    }
    

    Similarly if you have update any views on onFailure, do it on UI thread.