0

I'm trying to create a window which will display a listbox populated with an array parsed from JSON.

The listview is populated and created in the OnCreate method in my MainActivity. In the main activity I'm also calling an AsyncTask to parse the JSON array from a website.

The task is shown below:

    public class JSONParse extends AsyncTask<String, String, String[]> {
@Override
protected String[] doInBackground(String... params) {
    HttpURLConnection connection = null;
    BufferedReader reader = null;

    try {
        URL url = new URL(params[0]);
        connection = (HttpURLConnection) url.openConnection();
        connection.connect();

        InputStream IStream = connection.getInputStream();

        reader = new BufferedReader(new InputStreamReader(IStream));

        StringBuffer buffer = new StringBuffer();

        String recievedLine = "";

        while ((recievedLine = reader.readLine()) != null) {
            buffer.append(recievedLine);
        }

        String full = buffer.toString();


        JSONObject JSONParent = new JSONObject(full);
        JSONArray j_Puzzles = JSONParent.getJSONArray("PuzzleIndex");
        int arraySize = j_Puzzles.length();
        String[] s_Puzzles = new String[arraySize];

        StringBuffer endString = new StringBuffer();

        for (int i = 0; i < j_Puzzles.length(); i++) {
            s_Puzzles[i] = j_Puzzles.toString(i);
            endString.append(s_Puzzles[i] + "\n");
        }

        MainActivity.Waiting = true;
        return s_Puzzles;
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (JSONException e) {
        e.printStackTrace();
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
        try {
            if (reader != null) {

                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
}

What is the best way to get s_Puzzles out of the background thread and into my OnCreate so it can be used like this:

public class MainActivity extends AppCompatActivity {

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

    new JSONParse().execute("http://www.hull.ac.uk/php/349628/08309/acw/index.json");


    ListView listView = (ListView) findViewById(R.id.listView);

    ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, android.R.id.text1, s_Puzzles);

    listView.setAdapter(arrayAdapter);
}
}

Furthermore, am I going to have to pause the OnCreate method until the background worker is done in order to prevent the listview updating with nothing due to the worker thread not finishing.

Any help appreciated. Thanks

James
  • 356
  • 2
  • 13
  • If your AsyncTask is a subclass of your Activity, you can update your UI simply by setting the ListView in the `onPostExecute()` method of your AsyncTask. If the task is in its own class file, you'll need to use a callback to get the data back to your Activity. As for "pausing" the `onCreate()` method, that's not what you need to do. You need to simply show some sort of progress indicator while the background work is being done, then update the UI when it's finished. – NoChinDeluxe Apr 20 '16 at 22:26
  • Check out my answer [on this question](http://stackoverflow.com/questions/36166476/best-way-to-request-data-from-server/36166643#36166643) for more info about how to accomplish all this. – NoChinDeluxe Apr 20 '16 at 22:28
  • @NoChinDeluxe I did have it as a subclass for a while, so I reckon I can revert back to that quite easily. I want the list to be populated when the window is opened (I thought that was OnCreate), I haven't set an onPostExecute method as I don't have an input (like button clicks) yet? Or is my knowledge of these methods wrong? – James Apr 20 '16 at 22:31
  • `onPostExecute` has nothing to do with input buttons. It is used for updating UI after your AsyncTask job gets finished as the method runs in UI thread. – Shadab Ansari Apr 20 '16 at 22:34
  • It doesn't have to be a subclass. I personally like my tasks in separate files. But anyway, `onPostExecute()` is the method that you'll use to send the data to whomever is listening for it. It's where the return value of `doInBackground()` is sent. It runs on the UI thread, so you can use it to update UI. That's its main purpose. You can launch your task in `onCreate()`, but you'll have to show the user SOMETHING while you're waiting to get the data for the list. The most common way is to show a progress spinner. – NoChinDeluxe Apr 20 '16 at 22:35
  • Subclassing it sounds like a bad idea, because an asynchronous task isn't a type of activity. Inheritance is supposed to be a modeling technique, not a trick to make code reuse easier. – nasch Apr 20 '16 at 22:53

2 Answers2

0

Something like this will work, without the need to overcomplicate what you already have:

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

    final ListView listView = (ListView) findViewById(R.id.listView);
    final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1, new ArrayList<String>());

    listView.setAdapter(arrayAdapter);

    new JSONParse() {
        @Override
        protected void onPostExecute(String[] puzzles)
        {
            super.onPostExecute(puzzles);

            arrayAdapter.addAll(puzzles);
        }
    }.execute("http://www.hull.ac.uk/php/349628/08309/acw/index.json");
}
Allen G
  • 1,160
  • 6
  • 8
0

You are trying to call the AsyncTask and then you are setting the information when you are not certain that the information is loaded. Remember that it is being loaded Asynchronously. I recommend you to do that on the onPostExecute() method of the AsyncTask. An AsyncTask has 4 methods that you can customize:

  1. doInProgress: Send from the thread to the UI thread (Activity) the progress, normally a number. Here you can update the UI.
  2. onPreExecute: To setup things before the thread starts running. Here you can update the UI.
  3. doInBackground: You already have it, it's perfect. Here you cannot update the UI because this one runs on the background thread. It's the only method from the AsyncTask that doesn't run on the UI Thread (Activity).
  4. onPostExecute: You get the result from the doInBackground. Here is where you should update your UI.

You can find any tutorial for AsyncTask and its easy. In this project I use an AsyncTask for kind of the same. https://github.com/isaacurbina/ViewHolderListView/tree/master/app/src/main/java/com/mac/isaac/viewholderlistview

Hope it helps!

Isaac Urbina
  • 1,295
  • 11
  • 21