【问题标题】:How to save a file using a background thread in Android如何在 Android 中使用后台线程保存文件
【发布时间】:2013-11-19 09:28:42
【问题描述】:

我目前创建了一个使用 XmlSerializer 从对象创建 XML 文件的函数。我一直在研究使用不同形式的多线程,以便在 GUI 仍然可用且仍在更新时将文件保存在后台。我查看了使用 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();
    }
}

【问题讨论】:

    标签: android multithreading android-asynctask xmlserializer


    【解决方案1】:

    AsyncTask 类实现了一种最佳实践模式,用于将耗时(但寿命短)的处理移至后台线程并同步回 UI 线程,以便在完成时将更新应用到 UI。请注意,此类任务不会在 Activity 重新启动后持续存在,因此例如,如果您的设备方向发生变化,这些任务将被取消。

    但是,如果您不需要将 UI 更新为后台任务的一部分(这里似乎就是这种情况),那么只需使用实现起来更简单的普通 Thread 类(编辑:为从后台线程更新 UI):

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

    请注意,这种类型的线程在 Activity 重新启动时会持续存在,因此它通常应该运行到完成。

    要作为 AsyncTask 执行此操作(如果需要更新 UI,这可能是更好的选择)同样的事情可以通过以下方式实现:

    在您的活动中,创建 Async 类的实例并执行。

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

    将 AsyncTask 子类化为 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
     }
    }
    

    【讨论】:

    • 我认为你可能是对的。虽然我希望 GUI 显示文件已保存或有某种指示。但我希望该操作在单独的线程中执行。您是否建议使用线程函数创建一个新类,然后调用我已经创建的类或在函数中创建线程?
    • 查看我的编辑,了解如何从后台线程中更新 UI。我建议您在活动类中创建和管理线程并从那里调用您的函数。但我不想成为规定性的——试验一下,看看什么对你有用(并且看起来最干净的代码)。
    • 非常感谢您的帮助。 AsyncTask 似乎对我正在做的事情最有意义,并且我已经测试了这两种方法,AsyncTask 对我来说是最好的解决方案,也是最快的执行时间。
    • 关于处理程序方法:您不能在以不同方法定义的内部类中引用非最终变量handler
    【解决方案2】:

    NigelK 的回答中要补充的一点是,如果您使用 AsyncTask,您只能使用一次。所以你不能像下面这样调用它两次:

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

    相反,您需要执行以下操作:

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

    此外,如果您需要重复运行任务,您可能需要使用处理程序并在可运行对象中调用它,如下所示:

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

    查看这些链接。 AsyncTask Threading Rule - Can it really only be used once? Call asynctask in handler

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-30
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      相关资源
      最近更新 更多