4

I currently have created a function that uses XmlSerializer to create an XML file from objects. I have been researching about using different forms of multi threading in order to save the file in the background whilst the GUI is still usable and is still updating. I looked at using AsyncTask to do this, but am not to sure on what is the best way to implement it. Please can anyone help me with this and thank you in advance.

Here is the code that I have so far:

private String fileName;
private DataObjects dataObjects;

public SetCachedValuesFile() 
{

}

public void setFileName(String refFileName)
{
    fileName = refFileName;
}

public void setDataObjects(DataObjects refDataObjects)
{
    dataObjects = refDataObjects;
}

public String getFileName()
{
    return fileName;
}

public DataObjects getDataObjects()
{
    return dataObjects;
}

public void updateValues()
{
    ArrayList<DataObject> arrayListDataObject = dataObjects.getDataObjects();
    try 
    {
        /* Creates a new file and its directory. */
        File directory = new File(Environment.getExternalStorageDirectory() + "/XML_FILES/");
        directory.mkdirs();
        File newFile = new File(directory, fileName + ".xml");
        FileOutputStream fos = new FileOutputStream(newFile);

        /* Creates a new XML serializer which creates the structure of the XML file. */
        XmlSerializer serializer = Xml.newSerializer();
        serializer.setOutput(fos, "UTF-8");
        serializer.startDocument(null, true);
        serializer.startTag("", "CachedValues");
        for(DataObject dataObject : arrayListDataObject)
        {
            if(dataObject.getClass().equals(StringDataObject.class))
            {
                StringDataObject stringDataObject = (StringDataObject) dataObject;
                String address = HexFunctions.toString(stringDataObject.getAddress());
                String value = stringDataObject.getValue();

                serializer.startTag("", "DataObject");
                serializer.startTag("", "Address");
                serializer.text(address);
                serializer.endTag("", "Address");
                serializer.startTag("", "Value");
                serializer.text(value);
                serializer.endTag("", "Value");
                serializer.endTag("", "DataObject");

                System.out.println("String data object added to file.");
            }
            else if(dataObject.getClass().equals(IntDataObject.class))
            {
                IntDataObject intDataObject = (IntDataObject) dataObject;
                String address = HexFunctions.toString(intDataObject.getAddress());
                String value = Integer.toString(intDataObject.getValue());

                serializer.startTag("", "DataObject");
                serializer.startTag("", "Address");
                serializer.text(address);
                serializer.endTag("", "Address");
                serializer.startTag("", "Value");
                serializer.text(value);
                serializer.endTag("", "Value");
                serializer.endTag("", "DataObject");

                System.out.println("Int data object added to file.");
            }
            else if(dataObject.getClass().equals(FloatDataObject.class))
            {
                FloatDataObject floatDataObject = (FloatDataObject) dataObject;
                String address = HexFunctions.toString(floatDataObject.getAddress());
                String value = Float.toString(floatDataObject.getValue());

                serializer.startTag("", "DataObject");
                serializer.startTag("", "Address");
                serializer.text(address);
                serializer.endTag("", "Address");
                serializer.startTag("", "Value");
                serializer.text(value);
                serializer.endTag("", "Value");
                serializer.endTag("", "DataObject");

                System.out.println("Float data object added to file.");
            }
            else if(dataObject.getClass().equals(DoubleDataObject.class))
            {
                DoubleDataObject doubleDataObject = (DoubleDataObject) dataObject;
                String address = HexFunctions.toString(doubleDataObject.getAddress());
                String value = Double.toString(doubleDataObject.getValue());

                serializer.startTag("", "DataObject");
                serializer.startTag("", "Address");
                serializer.text(address);
                serializer.endTag("", "Address");
                serializer.startTag("", "Value");
                serializer.text(value);
                serializer.endTag("", "Value");
                serializer.endTag("", "DataObject");

                System.out.println("Double data object added to file.");
            }
        }
        serializer.endTag("", "CachedValues");
        serializer.endDocument();
        serializer.flush();
        fos.close();

        System.out.println("File created");
        System.out.println("File name: " + newFile.getAbsolutePath());
    } 
    catch (IllegalArgumentException e) 
    {
        e.printStackTrace();
    } 
    catch (IllegalStateException e) 
    {
        e.printStackTrace();
    }
    catch (IOException e) 
    {
        e.printStackTrace();
    }
}
James Meade
  • 1,147
  • 5
  • 22
  • 46

2 Answers2

11

The AsyncTask class implements a best practice pattern for moving time consuming (but short lived) processing to a background thread and synchronising back to the UI thread, to apply updates to the UI, when complete. Note that such tasks do not persist across Activity restarts so for example will be cancelled if the orientation of your device changes.

However, if you don't need to update the UI as part of your background task (which appears to be the case here), then just use the normal Thread class which is much simpler to implement (edit: code added for updating the UI from a background thread):

Handler handler = new Handler();  //Optional. Define as a variable in your activity.

Runnable r = new Runnable()
{
    @Override
    public void run()
    {
        // your code here
        handler.post(new Runnable()  //If you want to update the UI, queue the code on the UI thread
        {
            public void run()
            {
                //Code to update the UI 
            }
        });
    }
};

Thread t = new Thread(r);
t.start();

Note that this type of threading does persist across activity restarts so it should usually run to completion.

To do this as an AsyncTask (which may be a better choice if the UI needs to be updated) the same thing can be achieved by:

In your activity, create an instance of your Async class and execute.

SaveData save = new SaveData();
save.execute();

Subclass AsyncTask as a private class within your activity

private class SaveData extends AsyncTask<String, Void, Boolean>{

@Override
protected Boolean doInBackground(String... params) {
    // your background code here. Don't touch any UI components

    if(your code worked...)
        return true;                
    else
        return false;
}

protected void onPostExecute(Boolean result) {
     //This is run on the UI thread so you can do as you wish here
     if(result)
         Toast successful
     else
         Toast unsuccessful
 }
}
TooCool
  • 10,598
  • 15
  • 60
  • 85
NigelK
  • 8,255
  • 2
  • 30
  • 28
  • I think that you may be right. Although I will want the GUI to display that the file has been saved or have there be some sort of indication. But I want the operation to be performed in a separate thread. Would you suggest creating a new class with the thread function and then call the class that I have already created or create the thread in the function? – James Meade Nov 19 '13 at 11:10
  • See my edit for how to update the UI from inside a background thread. I recommend you create and manage the thread in your activity class and call your function from there. But I don't want to be prescriptive - experiment and see what works (and looks cleanest code wise) for you. – NigelK Nov 19 '13 at 11:32
  • Thank you very much for your help. AsyncTask seems to make the most sense with what I am doing and I have tested both methods and AsyncTask is the best solution for me and is the quickest execution time as well. – James Meade Nov 19 '13 at 14:08
  • With respect to handler method : you cannot refer to a non-final variable `handler` inside an inner class defined in a different method – Mayank Aug 26 '14 at 09:15
1

One thing to add to NigelK's answer is that if you use the AsyncTask you can only use it once. So you cannot call it twice as follows:

SaveData save = new SaveData();
save.execute();
//Later
save.execute();

Instead, you would need to do something like:

SaveData save1 = new SaveData();
save1.execute();
//Later
SaveData save2 = new SaveData();
save2.execute();

Additionally, if you need to run the task repetitively, you may want to use a handler and call it in the runnable like this:

Handler handler = new Handler();
Runnable runnable = new Runnable() {
    public void run() {
        SaveData save = new SaveData();
        save.execute();       
    }
};
handler.postDelayed(runnable, 500);

See these links. AsyncTask Threading Rule - Can it really only be used once? Call asynctask in handler

Community
  • 1
  • 1