-2

My app requires to parse a XML file when the activity is launched. So, I use an Asynctask to parse the XML.

After parsing the XML file, I get the count of number of test-cases in the XML in doInBackground() method, and I use the variable alTestCaseList to keep this count. Later in onPostExecute() method, I set the ArrayAdapter and register the click listener for the list.

However, when I click any testcase from the list, I'm supposed to parse the test-case entries from the XML again.

  1. So I believe for this I'll have to use an AsyncTask again. So do I start another AsyncTask for onPostExecute() method of first AsyncTask?
  2. Is there any other neat way of doing this?
  3. I tried to put setOnItemClickListener() in onCreate() method, but it resulted in fatal exception with the message: "setOnItemClickListener(android.widget.AdapterView$OnItemClickListener) on a null object reference"........

Kindly give your suggestion.

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

    xmlHelp = new XmlHelper();

    ConfigParser confParser = new ConfigParser();
    confParser.execute();
}

private class ConfigParser extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... params) {
        alTestCaseList = xmlHelp.getNumberOfNodes();
        return null;
    }

    @Override
    protected void onPostExecute(Void v) {
        testCasesListView = (ListView) findViewById(R.id.lstTestCases);
        arrayAdapter = new ArrayAdapter(TestCasesActivity.this, android.R.layout.simple_list_item_1, alTestCaseList);
        testCasesListView.setAdapter(arrayAdapter);
        testCasesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3) {
                String value = (String) adapter.getItemAtPosition(position);
                Log.d("QcTool", "Selected: " + value);
            }
        });
    }
}
soren.qvist
  • 7,376
  • 14
  • 62
  • 91
webgenius
  • 856
  • 3
  • 13
  • 30

2 Answers2

2

Here is my take on the solution. Pass your data as an argument to the task and notify the adapter when you get the list. See the comments for further explanation. Note that this approach does not handle issues that AsyncTask's typically come with is situations such as configuration changes.

You can then create another ParseXmlTask class which can be called in your OnItemClicked method

private ListView testCasesListView;
private ArrayAdapter arrayAdapter;
private List<String> testCasesList;

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

    //Init the list - it's empty but your task will fill it.
    testCasesList = new ArrayList<>();

    //Init your listView
    testCasesListView = (ListView) findViewById(R.id.lstTestCases);

    //Add adapter to the listView
    arrayAdapter = new ArrayAdapter(TestCasesActivity.this, android.R.layout.simple_list_item_1, alTestCaseList);

    //Add your click event
    testCasesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3) {
                //When clicked, do something awesome
                String value = (String) adapter.getItemAtPosition(position);
                Log.d("QcTool", "Selected: " + value);

                //Create and start parseXmlTask here
            }
        });

    xmlHelp = new XmlHelper();

    //Pass in your callback as an argument
    ConfigParserTask confParser = new ConfigParserTask(new OnConfigParserTaskCompleted(){
        public void onConfigParserTaskCompleted(List<String> result){
            //Simply refresh the list
            testCasesList.clear();
            testCasesList.addAll(result);
            //Let the adapter know that the list has changed
            //Then update the list view
            arrayAdapter.notifyDataSetChanged();
        }
    });
    confParser.execute(xmlHelp);
}

//It's better to pass in the info to the task as arguments than it is to rely on
//field variables 
private class ConfigParserTask extends AsyncTask<XmlHelper, Void, List<String>> {
    private OnConfigParserTaskCompleted listener;

    public ConfigParser(OnConfigParserTaskCompleted listener){
        this.listener = listener;
    }

    @Override
    protected Void doInBackground(XmlHelper... params) {
        //Do what you need to in the background
        //Get your nodes then return it here
        List<String> nodes = params[0].getNumberOfNodes();
        return nodes;
    }

    @Override
    protected void onPostExecute(List<String> result) {
        //pass the result to the callback 
       listener.onConfigParserTaskCompleted(result);
    }
}

//Callback to let your activity/fragment know when
//the task is complete
public interface OnConfigParserTaskCompleted{
    public void onConfigParserTaskCompleted(List<String> result);
}
CarefreeCrayon
  • 249
  • 2
  • 7
1

You can do something like this using AsyncTasks

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

    testCasesListView = (ListView) findViewById(R.id.lstTestCases);
    testCasesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3) {
            String value = (String) adapter.getItemAtPosition(position);
            Log.d("QcTool", "Selected: " + value);

            ParserNodeTask nodeTask = new ParserNodeTask();
            nodeTask.execute(value);
        }
    });

    xmlHelp = new XmlHelper();

    ParserNumberOfNodesTask numberOfNodesTask = new ParserNumberOfNodesTask();
    numberOfNodesTask.execute();
}

private class ParserNumberOfNodesTask extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... params) {
        alTestCaseList = xmlHelp.getNumberOfNodes();
        return null;
    }

    @Override
    protected void onPostExecute(Void v) {
        arrayAdapter = new ArrayAdapter(TestCasesActivity.this, android.R.layout.simple_list_item_1, alTestCaseList);
        testCasesListView.setAdapter(arrayAdapter);
    }
}

private class ParserNodeTask extends AsyncTask<Void, Void, Void> {
     @Override
    protected Void doInBackground(String... params) {
        String value = params[0];
        //TODO : parse the selected node
    }

    @Override
    protected void onPostExecute(Void v) {
        //TODO: dunno what you need to do later
    }
}

Though AsyncTasks are not ideal for this for many reasons (but easier to implement). You should maybe take a look on to Loaders or Services (i.e https://stackoverflow.com/a/6957909/665823)

Community
  • 1
  • 1
Kalem
  • 1,132
  • 12
  • 33
  • Thanks, this is really an elegant solution compared to my original approach. Really appreciate it! – webgenius Jul 31 '15 at 12:46
  • You should combine the concepts of `notifyDataSetChanged` pointed out by @CarefreeCrayon to have a even more elegant solution. Am a lazy guys so I didn't do it on my sample. – Kalem Jul 31 '15 at 14:18