2014-03-02 122 views
0

我想要自動保存每當用戶離開應用程序或更改爲其他活動時的主要活動。所以我在活動的OnPause方法中實現了save函數,這很好。問題是保存過程可能需要幾秒鐘,所以我希望在發生進度對話時進行。所以我用這樣的ASyncTask實現了這個功能:AsyncTask仍然阻止UI線程

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

     } 
    } 
} 

連載是這樣的:

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

我不是在改進保存/連載方法立刻感興趣,除非它們是相關的progressdialog問題。誰能幫忙?

+0

請將'dialog.show();'移動到'onPreExecute'的最後一行(全部使用對話框後)並再次測試, –

+2

「我可以在logcat中看到主線程仍然被阻塞」 - 我不明白這是如何實現的。你能提供有問題的LogCat條目並解釋你的分析嗎?除此之外,爲什麼你認爲你的表演 - 對話 - 暫停方法能夠正確工作並且首先成爲一個好主意? – CommonsWare

回答

1

我建議你AsyncTask以下變化:

首先從AsyncTask刪除進度場和無用的構造函數:

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

+0

我試過donfuxx的回答,再加上Shayan提出的改變,但沒有任何區別。 –

+0

我可以告訴主線程正在被阻塞,因爲logcat消息記錄在序列化代碼的開始和結束處,並且在這兩者之間,我可以看到來自Choreographer的消息,其中指出「跳過了128幀!應用程序可能是在其主線上做了太多的工作「 –

0

嘗試對話框移動到外部類開放它的AsyncTask外,並通過靜態方法

dialog = ProgressDialog.show(...) 

之前

task.execute(); 

應該可以將其關閉from onPostExecute