Reputation: 132
I my main activity I want to autosave whenever the user leaves the app or changes to another activity. So I implemented the save function in the activity's OnPause method, which works fine. Problem is the save process can take a few seconds, so I wanted a progressdialog while it happens. So I implenmeted that with an ASyncTask like this:
@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;
}
}
Problem is, the dialog doesn't appear until after the background task has completed. And I can see in logcat that the main thread is still being blocked. I believe it happens in the middle of the save function when it does the serialisation.
Save is like this:
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 is like this:
@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;
}
I'm not immediately interested in improvements to the save/serialise methods unless they are relevant to the progressdialog problem. Can anyone help?
Upvotes: 0
Views: 2600
Reputation: 349
Try moving the dialog to the outer class and opening it outside the AsyncTask and by the static method
dialog = ProgressDialog.show(...)
just before
task.execute();
It should be possible to close it from onPostExecute
Upvotes: 0
Reputation: 11323
I propose the following change of your AsyncTask
:
First of all remove the progressbar field and the useless constructor from the AsyncTask
:
private ProgressDialog dialog; //should be declared as field in Activity
public SaveTask(PlayCharacterActivity activity) { //don't pass the activity to AsyncTask
}
Then in the onPreExecute() just do:
@Override
protected void onPreExecute() {
showDialog(); //call a method in your Activity that shows your dialog as you want
}
Remember that AsyncTask's onPreExecute method is run in the UI thread so you can deal with views from the Activity here. See documentation: http://developer.android.com/reference/android/os/AsyncTask.html#onPreExecute()
Upvotes: 1