Как сохранить файл с помощью фонового потока в Android

В настоящее время я создал функцию, которая использует XmlSerializer для создания файла XML из объектов. Я исследовал использование различных форм многопоточности, чтобы сохранить файл в фоновом режиме, в то время как графический интерфейс все еще можно использовать и все еще обновляет. Я рассмотрел использование AsyncTask для этого, но не уверен, как лучше всего это реализовать. Пожалуйста, помогите мне с этим и заранее спасибо.

Вот код, который у меня есть до сих пор:

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();
    }
}

person James Meade    schedule 19.11.2013    source источник


Ответы (2)


Класс AsyncTask реализует передовой шаблон для перемещения трудоемкой (но недолговечной) обработки в фоновый поток и обратной синхронизации с потоком пользовательского интерфейса для применения обновлений к пользовательскому интерфейсу по завершении. Обратите внимание, что такие задачи не сохраняются при перезапуске Activity, поэтому, например, они будут отменены, если изменится ориентация вашего устройства.

Однако, если вам не нужно обновлять пользовательский интерфейс как часть вашей фоновой задачи (что, по-видимому, имеет место здесь), просто используйте обычный класс 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();

Обратите внимание, что этот тип многопоточности сохраняется при перезапуске активности, поэтому обычно он должен выполняться до завершения.

Чтобы сделать это как AsyncTask (что может быть лучшим выбором, если пользовательский интерфейс необходимо обновить), то же самое можно сделать следующим образом:

В своей деятельности создайте экземпляр класса Async и выполните его.

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

Подкласс AsyncTask как частный класс в вашей деятельности

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
 }
}
person NigelK    schedule 19.11.2013
comment
Я думаю, что вы можете быть правы. Хотя я хочу, чтобы графический интерфейс отображал, что файл был сохранен, или имел какую-то индикацию. Но я хочу, чтобы операция выполнялась в отдельном потоке. Не могли бы вы предложить создать новый класс с функцией потока, а затем вызвать класс, который я уже создал, или создать поток в функции? - person James Meade; 19.11.2013
comment
См. мое редактирование, чтобы узнать, как обновить пользовательский интерфейс из фонового потока. Я рекомендую вам создать поток и управлять им в своем классе активности и вызывать оттуда свою функцию. Но я не хочу быть предписывающим - поэкспериментируйте и посмотрите, что работает (и выглядит самым чистым кодом) для вас. - person NigelK; 19.11.2013
comment
Спасибо большое за помощь. AsyncTask, кажется, лучше всего подходит для того, что я делаю, и я протестировал оба метода, и AsyncTask — лучшее решение для меня, а также самое быстрое время выполнения. - person James Meade; 19.11.2013
comment
Что касается метода обработчика: вы не можете ссылаться на неконечную переменную handler внутри внутреннего класса, определенного в другом методе. - person Mayank; 26.08.2014

Одна вещь, которую следует добавить к ответу Найджела К., заключается в том, что если вы используете AsyncTask, вы можете использовать его только один раз. Таким образом, вы не можете вызвать его дважды следующим образом:

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

Вместо этого вам нужно будет сделать что-то вроде:

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

Кроме того, если вам нужно повторно запускать задачу, вы можете использовать обработчик и вызвать его в runnable следующим образом:

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

Посмотрите эти ссылки. Правило AsyncTask Threading. Можно ли его использовать только один раз? Вызов асинхронной задачи в обработчике

person Community    schedule 09.02.2017