Search code examples
androidprogressdialogloopjasynchttpclient

Properly manage ProgressDialog in AsyncHttpClient tasks to avoid leaked windows Android


I am developing an android application.

I need to make several requests to a server from my app so I am using AsyncHttpClient.

A part of my app has an user profile and a timeline to show some events. When the user logs in, I need to get their profile information, and the information of their timeline and to do so I have to make 3 different requests to the server:

1st Request: Log in -> Save cookies and session information into SharedPreferences 2nd Request: Get profile -> Save personal information of the user. 3rd Request: Get the user timeline -> Save posts and events related to the current user.

Here is my logIn request:

public static void login(final String email, final String password,
            final Context context, final Context appContext, final Resources res) {

        prgDialog = new ProgressDialog(context);
        prgDialog.setMessage(res.getString(R.string.dialog_please_wait));
        prgDialog.setCancelable(false);
        prgDialog.show();
        cookieStore = new PersistentCookieStore(appContext);
        client.setCookieStore(cookieStore);

        RequestParams params = new RequestParams();
        params.put("user_session[email]", email);
        params.put("user_session[password]", password);

        client.addHeader("Accept", HEADER);

        client.post(getAbsoluteUrl(LOGIN_PATH), params,
                new JsonHttpResponseHandler() {

                    @Override
                    public void onFailure(int statusCode,
                            org.apache.http.Header[] headers,
                            java.lang.String responseString,
                            java.lang.Throwable throwable) {
                        prgDialog.hide();
                        if (statusCode == 404) {
                            Toast.makeText(context,
                                    res.getString(R.string.error_404),
                                    Toast.LENGTH_LONG).show();
                        } else if (statusCode == 500) {
                            Toast.makeText(context,
                                    res.getString(R.string.error_500),
                                    Toast.LENGTH_LONG).show();
                        } else if (statusCode == 401) {
                            Toast.makeText(context,
                                    res.getString(R.string.login_401),
                                    Toast.LENGTH_LONG).show();
                        } else {
                            Toast.makeText(
                                    context,
                                    "Unexpected Error occcured! [Most common Error: Device might not be connected to Internet or remote server is not up and running]",
                                    Toast.LENGTH_LONG).show();
                        }
                    }

                    @Override
                    public void onSuccess(int statusCode, Header[] headers,
                            JSONObject response) {
                        if (statusCode == 200) {
                            // In this case the JSONOBject has the user
                            // credentials, such as user_id, person_id
                            // user_instance_type and user_instance_id
                            // Parse them into an object that has the same
                            // attributes
                            Gson gson = new Gson();
                            UserCredentials userCredentials = gson.fromJson(
                                    response.toString(), UserCredentials.class);

                            setInitialPrefs(userCredentials, appContext);

                            // Get the user profile and save into the
                            // database
                            getUserProfile(userCredentials.getUser_id(),
                                    context, appContext, prgDialog);

                            // Get the timeline
                            getWalls(true, context, appContext, prgDialog);
                        }

                    }
                });
    }

both methods, getUserProfile and getWalls are asynchronous requests themselves. Here's the code:

public static void getUserProfile(int userId, final Context context,
            final Context appContext, final ProgressDialog prgDialog) {

        prgDialog.show();

        cookieStore = new PersistentCookieStore(appContext);
        client.setCookieStore(cookieStore);
        client.addHeader("Accept", HEADER);

        client.get(getAbsoluteUrl(USERS_PATH + userId),
                new JsonHttpResponseHandler() {
                    @Override
                    public void onFailure(int statusCode,
                            org.apache.http.Header[] headers,
                            java.lang.String responseString,
                            java.lang.Throwable throwable) {
                        prgDialog.hide();
                        if (statusCode == 404) {
                            Log.d(TAG, "404 getting profile");
                        } else if (statusCode == 500) {
                            Toast.makeText(
                                    context,
                                    context.getResources().getString(
                                            R.string.error_500),
                                    Toast.LENGTH_LONG).show();
                            } else if (statusCode == 401) {
                            Log.d(TAG, "401 getting profile");
                        } else {
                            Log.d(TAG, "Error getting profile");
                        }
                    }

                    @Override
                    public void onSuccess(int statusCode, Header[] headers,
                            JSONObject response) {

                        if (statusCode == 200) {
                            // In this case the JSONOBject has the user
                            // profile
                            // Parse them into an object that has the same
                            // attributes
                            Gson gson = new Gson();
                            UserProfile userProfile = gson.fromJson(
                                    response.toString(), UserProfile.class);
                            UserProfileController profileController = new UserProfileController(
                                    context);
                            profileController.insertProfile(userProfile);
                        }

                    }

                });
    }

public static void getWalls(final boolean firstTime, final Context context,
            Context appContext, final ProgressDialog prgDialog) {
        cookieStore = new PersistentCookieStore(appContext);

        prgDialog.show();
        client.setCookieStore(cookieStore);
        client.addHeader("Accept", HEADER);

        client.get(getAbsoluteUrl(WALLS_PATH), new JsonHttpResponseHandler() {
            @Override
            public void onFailure(int statusCode,
                    org.apache.http.Header[] headers,
                    java.lang.String responseString,
                    java.lang.Throwable throwable) {
                prgDialog.hide();
                if (statusCode == 404) {
                    Log.d(TAG, "404 getting walls");
                } else if (statusCode == 500) {
                    Toast.makeText(
                            context,
                            context.getResources().getString(
                                    R.string.error_500),
                            Toast.LENGTH_LONG).show();
                    } else if (statusCode == 401) {
                    Log.d(TAG, "401 getting walls");
                } else {
                    Log.d(TAG, "Error getting walls");
                }
            }

            @Override
            public void onSuccess(int statusCode, Header[] headers,
                    JSONObject response) {
                if (statusCode == 200) {
                    Gson gson = new Gson();

                    TimelineController.getInstance(context);

                    Timeline timeline = gson.fromJson(response.toString(),
                            Timeline.class);

                    TimelineController.insertTimeline(timeline);

                    if (firstTime) {
                        prgDialog.hide();
                        Intent i = new Intent(context, TimelineActivity.class);
                        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
                                | Intent.FLAG_ACTIVITY_CLEAR_TASK
                                | Intent.FLAG_ACTIVITY_NEW_TASK);
                        context.startActivity(i);
                        ((AuthActivity) context).finish();
                    } else {
                        prgDialog.hide();
                        Intent i = new Intent(context, TimelineActivity.class);
                        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        context.startActivity(i);
                    }
                }
            }

        });
    }

If you see the code, what I am trying to do with the progress dialog is to keep it shown until the last request finishes (the getWalls request)

Thing is, sometimes when I log out and log in again with the same or a different user, I get the android.view.WindowLeaked exception and I think it is because I am not managing well my progress dialog.

How can I properly manage my progress dialog to avoid getting leaked windows?

Hope anyone can help me with this, thanks in advance.


Solution

  • Assume the problem is the progress dialogue, you may try to use one of the static show method. The following code makes sure that the progress dialogue is not double or triple showing:

    if (progressDialog == null || !progressDialog.isShowing()) {
        progressDialog = ProgressDialog.show(activity, "Changing password ...", "Please wait.", true, false);
    }
    

    And you define:

    private ProgressDialog progressDialog;