1

I'm developing an android app that reads data from API then show these data in multi-choice dialog. However, when I click the button the dialog is empty. I think that maybe the program executes the next lines of code before volley returns the response. I searched a lot but I didn't understand how to fix this, I'm a beginner in using Volley.

Here is the dialog code where I get the data from API:

public class LevelsDialog extends DialogFragment {
    ArrayList<String> list = new ArrayList<>();
    static   String[] array;
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        array = getLevels();
        final boolean[] checkedItems = new boolean[array.length];
        builder.setTitle("Select Levels").setMultiChoiceItems(array, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                if(isChecked) {
                    list.add(array[which]);
                    checkedItems[which] =true;
                } else if(list.contains(array[which])) {
                    list.remove(array[which]);
                    checkedItems[which] = false;
                }
            }

        }).setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                for(int i=0 ; i<list.size(); i++)
                Log.e("After", "onClick: "+ list.get(i) );
            }
        });

        return builder.create();
    }

    public String[] getLevels() {
        final ArrayList<String> LevelsList = new ArrayList<>();

        String url = Connection.URL + "/getAllLevels";

        JsonArrayRequest objectRequest = new JsonArrayRequest(
                Request.Method.GET,
                url,
                null,
                new Response.Listener<JSONArray>() {

                    @Override
                    public void onResponse(JSONArray response) {
                        if (response.length() != 0) {
                            int length = response.length();

                            for (int i = 0; i < length; i++) {
                                try {
                                    LevelsList.add(response.get(i).toString());

                                } catch (JSONException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                },
                new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e("Rest Response Error ", error.toString());
                    }
                });
        Connection.getConnection(getActivity()).addToRequestQueue(objectRequest);

        String[] Levels = LevelsList.toArray(new String[LevelsList.size()]);

        return Levels;
    }
}

Any idea? Thanks.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
ROM
  • 153
  • 3
  • 16
  • "I think that maybe the program executes the next lines of code before volley returns the response." - Exactly right. If the data in your `Dialog` relies on that response, you should instead queue the request first, then show the `Dialog` when it returns, in `onResponse()`. – Mike M. Mar 03 '18 at 01:05
  • Make you call to API in the onCreate method of screen where dialog should be displayed. Then use obtained data for create this dialog when it necessary. – Eugene Troyanskii Mar 03 '18 at 02:02

2 Answers2

1

The method public String[] getLevels() cannot return instantly the array, because you are doing a request to the internet (and this takes time), so, you need to use a callback interface, when the volley complete the request, it will call it back and only in this time you will write in the screen

The generic callback:

public interface ICallback<T> {
    void onSucess(T result);
    void onError(int code);
}

The method getLevels will be void, and with a callback parameter

public void getLevels(final ICallback<String[]> callback) {
    String url = Connection.URL + "/getAllLevels";

    JsonArrayRequest objectRequest = new JsonArrayRequest(
            Request.Method.GET,
            url,
            null,
            new Response.Listener<JSONArray>() {

                @Override
                public void onResponse(JSONArray response) {
                    final ArrayList<String> LevelsList = new ArrayList<>();
                    if (response.length() != 0) {
                        int length = response.length();

                        for (int i = 0; i < length; i++) {
                            try {
                                LevelsList.add(response.get(i).toString());

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    String[] Levels = LevelsList.toArray( 
                                              new String[LevelsList.size()]);
                    callback.onSucess(Levels); //<-------- CALL IT BACK
                }
            },
            new Response.ErrorListener() {

                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("Rest Response Error ", error.toString());
                    int code = -1;
                    if(error!=null){
                         if(error.networkResponse!=null){
                              code = error.networkResponse.statusCode;
                         }
                    }
                    callback.onError(code); // <-- ERROR
                }
            });
    Connection.getConnection(getActivity()).addToRequestQueue(objectRequest);

}

So, you will have the levels only in the callback:

getLevels(
      new ICallback<String[]>(){
            @Override
            public void onSucess(String[] result) {
                  // Create or update the dialog here...
            }
            @Override
            public void onError(int errorCode) {
                  //error treatment...
            }
      }
);
Rodrigo João Bertotti
  • 5,179
  • 2
  • 23
  • 34
  • You will create `LevelsDialog` only when the method `onSucess(String[] result)` was called. You need put the code of constructing the `LevelsDialog` (that you have not showed here) inside `onSucess(String[] result)`, after this, you will have to pass the array of strings `result` to `LevelsDialog` on the constructor, or another way you prefer – Rodrigo João Bertotti Mar 03 '18 at 19:45
0

You need to wait for the data to be received from your volley request and then allow the users to hit the multi-choice dialog.

I would like to suggest keeping a ProgressDialog in your case, which will be shown until the level data is received. Hence, your onCreateDialog will be looking something like this.

private ProgressDialog pD;
private final boolean[] checkedItems;

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    pD = new ProgressDialog(context); 
    pD.setMessage("Getting the levels information");

    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle("Select Levels").setMultiChoiceItems(array, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which, boolean isChecked) {
            if(isChecked) {
                list.add(array[which]);
                checkedItems[which] =true;
            } else if(list.contains(array[which])) {
                list.remove(array[which]);
                checkedItems[which] = false;
            }
        }

    }).setPositiveButton("OK", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            for(int i=0 ; i<list.size(); i++)
            Log.e("After", "onClick: "+ list.get(i) );
        }
    });


    getLevels();
    pD.show(); // Start showing the progress dialog

    return builder.create();
}

Now modify your getLevels function like this.

 public void getLevels() {
    final ArrayList<String> LevelsList = new ArrayList<>();

    String url = Connection.URL + "/getAllLevels";
    JsonArrayRequest objectRequest = new JsonArrayRequest(
            Request.Method.GET, url, null, new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) {
                    if (response.length() != 0) {
                        int length = response.length();
                        array = new int[length]; // Re-initialize the array

                        for (int i = 0; i < length; i++) {
                            try {
                                array[i] = response.get(i).toString();
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }

                        pD.cancel();
                        checkedItems = new boolean[array.length];
                    }
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("Rest Response Error ", error.toString());
                }
            });
    Connection.getConnection(getActivity()).addToRequestQueue(objectRequest);
}

Hope that helps.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98