Android AsyncTask仍在阻止UI线程

Android AsyncTask仍在阻止UI线程,android,android-asynctask,Android,Android Asynctask,我希望在用户离开应用程序或更改为其他活动时自动保存我的主要活动。因此,我在活动的OnPause方法中实现了save函数,效果很好。问题是保存过程可能需要几秒钟,所以我希望在保存过程中出现progressdialog。因此,我建议使用如下异步任务: @Override public void onPause() { super.onPause(); // save sheet when user is leaving activity SaveTask task = ne

我希望在用户离开应用程序或更改为其他活动时自动保存我的主要活动。因此,我在活动的OnPause方法中实现了save函数,效果很好。问题是保存过程可能需要几秒钟,所以我希望在保存过程中出现progressdialog。因此,我建议使用如下异步任务:

@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;
    }

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

        }
    }
}
@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;
}
Serialise是这样的:

@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;
    }

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

        }
    }
}
@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问题相关。有人能帮忙吗?

我建议对您的
异步任务进行以下更改:

private ProgressDialog dialog; //should be declared as field in Activity

public SaveTask(PlayCharacterActivity activity) { //don't pass the activity to 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线程中运行的,因此您可以在此处处理活动中的视图。请参阅文档:

尝试将对话框移动到外部类,并在AsyncTask外部通过静态方法打开它

dialog = ProgressDialog.show(...)
就在之前

task.execute();

应该可以从onPostExecute关闭它

请移动
对话框。show()
到onPreExecute的最后一行(使用dialog完成所有工作后),然后再次测试,“我可以在logcat中看到主线程仍然被阻塞”——我不知道这是怎么可能的。你能提供有问题的LogCat条目并解释你的分析吗?除此之外,为什么你认为你的“对话时暂停”方式能够正确工作,并且首先是个好主意?我尝试了donfuxx的答案,并做出了Shayan建议的改变,但这没什么区别。我可以看出主线程被阻塞了,因为在序列化代码的开头和结尾都有logcat消息,在这两个消息之间,我可以看到Choreographer的一条消息,它说“跳过了128帧!应用程序可能在主线程上做了太多工作”