AsyncTask仍然阻止UI线程

时间:2014-03-02 11:41:28

标签: android android-asynctask

我的主要活动我想在用户离开应用程序或更改其他活动时自动保存。所以我在activity的OnPause方法中实现了save函数,该方法工作正常。问题是保存过程可能需要几秒钟,所以我想要一个progressdialog,当它发生时。所以我用这样的ASyncTask实现了这个:

@Override
public void onPause()
{
    super.onPause();

    // save sheet when user is leaving activity
    SaveTask task = new SaveTask(PlayCharacterActivity.this);

    task.execute();
}

private class SaveTask extends AsyncTask <Void, Void, Void> {
    private ProgressDialog dialog;

    public SaveTask(PlayCharacterActivity activity) {

    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        dialog = new ProgressDialog(PlayCharacterActivity.this);
        dialog.setMessage("Saving...");
        dialog.show();
        dialog.setIndeterminate(true);
        dialog.setCancelable(false);

    }

    @Override
    protected void onPostExecute(Void result) {
        if (dialog.isShowing()) {
            dialog.dismiss();
        }
        super.onPostExecute(result);
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            //Log.d("plch:OnPause", "starting save");
            Sheet.getInstance().Save();
            //Log.d("plch:OnPause", "finished save");
        } catch (RuntimeException e) {
            e.printStackTrace();
        }

        return null;
    }

}

问题是,直到后台任务完成后才会出现对话框。我可以在logcat中看到主线程仍然被阻止。我相信它在执行序列化时会在保存函数的中间发生。

保存是这样的:

public void Save()
{       
    long dtMili = System.currentTimeMillis();
    Date d = new Date(dtMili);
    CharSequence s  = DateFormat.format("hh:mm:ss", d.getTime());

    // use this flag to know whether to back up the saved file or not
    boolean saveSuccessful = false;

    //_xml = new String("");
    dtMili = System.currentTimeMillis();
    d = new Date(dtMili);
    Log.d("Load/Save", "started serialising: " + DateFormat.format("hh:mm:ss", d.getTime()));
    _xml = _instance.Serialise();
     dtMili = System.currentTimeMillis();
    d = new Date(dtMili);
    Log.d("Load/Save", "finished serialising: " + DateFormat.format("hh:mm:ss", d.getTime()));
    try
    {
        //---SD Card Storage---
        File sdCard = Environment.getExternalStorageDirectory();
        File directory = new File (sdCard.getAbsolutePath() + "/RPGenius");
        directory.mkdirs();
        File file = new File(directory, _name + ".rpg");
        FileOutputStream fOut = new FileOutputStream(file);
        Log.d("Saving to: ", file.getAbsolutePath());

        //---write the string to the file---
        OutputStreamWriter osw = new OutputStreamWriter(fOut);
        //DebugHelper.DebugMessage("File contents: " + _xml);

        dtMili = System.currentTimeMillis();
        d = new Date(dtMili);
        Log.d("Load/Save", "started writing file: " + DateFormat.format("hh:mm:ss", d.getTime()));

        osw.write(_xml);
        osw.flush();
        osw.close();

        dtMili = System.currentTimeMillis();
        d = new Date(dtMili);
        Log.d("Load/Save", "finished writing file: " + DateFormat.format("hh:mm:ss", d.getTime()));

        saveSuccessful = true;
    }
    catch (NullPointerException npe)
    {
        npe.printStackTrace();
    }
    catch (IOException ioe)
    {
        ioe.printStackTrace();
    }

    // if the save was completely successfully, back it up
    if (saveSuccessful)
    {
        File sdCard = Environment.getExternalStorageDirectory();
        File directory = new File (sdCard.getAbsolutePath() + "/RPGenius");
        File file = new File(directory, _name + ".rpg");

        if (file.exists())
        {
            // locate backup directory and create if not present
            File backupDirectory = new File (sdCard.getAbsolutePath() + "/RPGenius/Backups");
            backupDirectory.mkdirs();

            // create target file location/name
            File backupFile = new File(backupDirectory, _name + ".rpg");

            // attempt to copy file
            try {
                FileInputStream inStream = new FileInputStream(file);
                FileOutputStream outStream = new FileOutputStream(backupFile);
                FileChannel inChannel = inStream.getChannel();
                FileChannel outChannel = outStream.getChannel();
                inChannel.transferTo(0, inChannel.size(), outChannel);
                inStream.close();
                outStream.close();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }
}

Serialise是这样的:

@Override
public String Serialise() {
    // produce xml string to represent this object
    _indents = 0;
    _xml = new String("");
    StartXmlItem("Sheet");
    //AddSimpleXmlItem("Name", _name);

    StartXmlItem("Stats");
    for (XmlSerialisable stat: _stats)
    {
        AddComplexXmlItem(stat);
    }
    EndXmlItem("Stats");

    StartXmlItem("Effects");
    for (XmlSerialisable effect: _effects)
    {
        AddComplexXmlItem(effect);
    }
    EndXmlItem("Effects");

    StartXmlItem("Pages");
    for (XmlSerialisable page: _pages)
    {
        AddComplexXmlItem(page);
    }
    EndXmlItem("Pages");

    EndXmlItem("Sheet");

    return _xml;
}

我不会立即对save / serialise方法的改进感兴趣,除非它们与progressdialog问题相关。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:1)

我建议您对AsyncTask进行以下更改:

首先从AsyncTask

中删除progressbar字段和无用的构造函数
private ProgressDialog dialog; //should be declared as field in Activity

public SaveTask(PlayCharacterActivity activity) { //don't pass the activity to AsyncTask

}

然后在onPreExecute()中执行:

@Override
protected void onPreExecute() {
     showDialog(); //call a method in your Activity that shows your dialog as you want
}

请记住,AsyncTask的onPreExecute方法在UI线程中运行,因此您可以在此处理Activity中的视图。请参阅文档:http://developer.android.com/reference/android/os/AsyncTask.html#onPreExecute()

答案 1 :(得分:0)

尝试将对话框移动到外部类并在AsyncTask外部以静态方法

打开它
dialog = ProgressDialog.show(...)

之前

task.execute();

应该可以从onPostExecute

关闭它