取消使用 ProgressDialog 的 AsyncTask
我有一个 AsyncTask,它从 DoinBackground 启动 DatabaseHelper 类,该类将 SQLite 数据库从 /assets 目录复制到应用程序目录 (/databases)。
在 preExecute() 中,我启动了一个 ProgressDialog。当 DB 帮助程序类的各个部分完成时,DoinBackground 进程会更新 ProgressDialog。
据我了解,当设备旋转时,我需要关闭对话框,取消 AsyncTask,然后在旋转完成后在 onResume() 中再次重新启动两者。
第一个问题是当我调用 AsyncTask.cancel() 时,我的 onCancel() 事件触发,但 AsyncTask 继续运行。我知道,因为 LogCat 在轮换完成后很久就显示了我的 DB Helper 的输出。用户界面在旋转后可用,因为进度对话框消失了,但数据库似乎仍然在复制,所以这不好。
我在谷歌上搜索到的一条信息说你无法取消 AsyncTask,所以我是否可以让该任务在后台运行而不用担心它?有没有办法将(仍在执行的)DoinBackground 进程再次连接到progressDialog?
谢谢
package com.test.helloasync;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
public class HelloAsync extends Activity
{
myVars v;
protected fileCopyTask fct;
private final String TAG = "** HelloAsync **";
private final String DBNAME = "foo.png"; // need png extension: Data exceeds UNCOMPRESS_DATA_MAX (5242880 vs 1048576)
@Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
setContentView (R.layout.main);
restoreFromObject ();
}
/* (non-Javadoc)
* @see android.app.Activity#onResume()
*/
@Override
protected void onResume ()
{
// TODO Auto-generated method stub
Log.d (TAG, "onResume()");
// only start NEW task if not cancelled and file has not been copied yet
if ((v.taskCancelled == false) && (v.fileCopied == false))
{
fct = new fileCopyTask ();
fct.execute ();
}
// show progressdialog if we were cancelled becuase asynctask will continue by itself
if ((v.taskCancelled == true) && (v.fileCopied == false))
{
Log.d (TAG, "onAttachedToWindow(): launching fct");
v.pd.show ();
// may need to check status here to make sure it was cancelled and not something else...
Log.d (TAG, "fct cancel status is " + fct.isCancelled ());
}
super.onResume ();
}
/**
* saves myVars class during rotation
*/
@Override
public Object onRetainNonConfigurationInstance ()
{
Log.d (TAG, "onRetainNonConfigurationInstance(): saving myVars objects");
// close db transfer dialogs if showing so we don't cause a UI error
if (v.pd != null)
if (v.pd.isShowing ())
{
v.taskCancelled = true;
v.pd.cancel ();
}
// save task to myVars so we can use it onRestoreInstanceState
v.fct = fct;
return (v);
}
/*
* restores myVars class after rotation
*/
private void restoreFromObject ()
{
Log.d (TAG, "restoreFromObject(): retrieving myVars object");
v = (myVars) getLastNonConfigurationInstance ();
// initialize myVars object (v) first time program starts
if (v == null)
v = new myVars ();
else
{
Log.d (TAG, "myVars already initialized");
fct = (fileCopyTask) v.fct;
}
}
/**
*
* copy a file from /assets to /data/data/com.blah.blah/databases
*
*/
private class fileCopyTask extends AsyncTask<Integer, Void, Void>
{
// on UI thread here
protected void onPreExecute ()
{
Log.d (TAG, "onPreExecute()" );
// only show this when db has not been copied
// set initDbDone to false prir to call if downloading a new DB
v.pd = new ProgressDialog (HelloAsync.this);
v.pd.setProgressStyle (ProgressDialog.STYLE_HORIZONTAL);
v.pd.setMessage ("Initializing Database");
v.pd.show ();
}
/**
* opens file in assets/ directory and counts the bytes in it so we can have an actual progress bar
*
* @return size
*/
private int getAssetSize ()
{
int size = 0;
try
{
InputStream fin = getBaseContext ().getAssets ().open (DBNAME);
byte [] buffer = new byte [1024];
int length = 0;
while ((length = fin.read (buffer)) > 0)
size += length;
fin.close ();
}
catch (IOException ioe)
{
Log.d (TAG, "fileCopyTask(): asset size failed: ioex :" + ioe);
size = 0;
}
Log.d (TAG, " fileCopyTask(): asset size is " + size);
return (size);
}
@Override
protected Void doInBackground (Integer... params)
{
Log.d (TAG, "doInBackground: +++++++++++++++++++++++++++++++++");
try
{
int inputsize = getAssetSize ();
// this is the input file in the assets directory. We have no idea how big it is.
InputStream fin = getBaseContext ().getAssets ().open (DBNAME);
// this is the destination database file on the android device
File dbFile = getBaseContext ().getDatabasePath (DBNAME);
// check if destination directory exists
String parent = dbFile.getParent ();
// create the desitination directory if it does not exist in /data/data/blah.. (on device)
if (dbFile.exists () == false)
{
boolean result = new File (parent).mkdir ();
Log.d (TAG, " fileCopyTask(): mkdir() result is " + result);
}
// this is the output file in the databases/ subdirectory of the app directory (on device)
OutputStream fout = new FileOutputStream (dbFile);
// transfer bytes from the inputfile to the outputfile
byte [] buffer = new byte [4096];
int length;
int bytesCopied = 0;
while ((length = fin.read (buffer)) > 0)
{
fout.write (buffer, 0, length);
bytesCopied += length;
float b = (float) (bytesCopied * 1.0);
float i = (float) (inputsize * 1.0);
float pctdone = (float) ((b / i) * 100.0);
int pct = (int) (pctdone);
// updating every 4k seems to really slow things down so do it every 5 chunks
if ((pct % 5) == 0)
v.pd.setProgress ((int) pctdone);
}
fout.flush ();
fin.close ();
fout.close ();
}
catch (IOException e)
{
Log.d (TAG, "fileCopyTask(): DB copy blew up. IOE: " + e.getMessage ());
// just in case, attempt to cancel the process in event of cataclysm
this.cancel (true);
e.printStackTrace ();
}
return null;
}
// can use UI thread here
protected void onPostExecute (final Void unused)
{
Log.d (TAG, "fileCopyTask():onPostExecute()");
// set progress bar to 100% when done.
v.pd.setProgress (100);
v.pd.dismiss ();
// set the state flags to show a succesful completion
v.taskCancelled = false;
v.fileCopied = true;
}// onPostExecute()
}// fileCopyTask()
}
///////////////////////////////////////
// myVars.java
//////////////////////////////////////
/**
*
*/
package com.test.helloasync;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.ProgressDialog;
import android.content.DialogInterface;
public class myVars
{
ProgressDialog pd;
boolean taskCancelled = false;
boolean fileCopied = false;
Object fct;
}
I have an AsyncTask which launches a DatabaseHelper class from DoinBackground which copies an SQLite database from the /assets directory to the application directory (/databases).
In preExecute(), I startup a progressDialog. As pieces of the DB helper class complete, the DoinBackground process updates the progressDialog.
When the device is rotated, as I understand it, I need to dismiss the dialog box, cancel the AsyncTask, and then restart both again in onResume(), after the rotation is finished.
The first problem is when I call AsyncTask.cancel(), my onCancel() event fires, but the AsyncTask keeps running. I know because LogCat is showing output from my DB Helper long after the rotation is finished. The UI is usable after the rotation because the progressDialog is gone but the DB still appears to be mucking around copying so this is no good.
One piece of info I googled said you can't cancel an AsyncTask, so do I just let the thing run in the background on not worry about it? Is there a way to hook up the (still executing) DoinBackground process to the progressDialog again?
Thanks
package com.test.helloasync;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
public class HelloAsync extends Activity
{
myVars v;
protected fileCopyTask fct;
private final String TAG = "** HelloAsync **";
private final String DBNAME = "foo.png"; // need png extension: Data exceeds UNCOMPRESS_DATA_MAX (5242880 vs 1048576)
@Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
setContentView (R.layout.main);
restoreFromObject ();
}
/* (non-Javadoc)
* @see android.app.Activity#onResume()
*/
@Override
protected void onResume ()
{
// TODO Auto-generated method stub
Log.d (TAG, "onResume()");
// only start NEW task if not cancelled and file has not been copied yet
if ((v.taskCancelled == false) && (v.fileCopied == false))
{
fct = new fileCopyTask ();
fct.execute ();
}
// show progressdialog if we were cancelled becuase asynctask will continue by itself
if ((v.taskCancelled == true) && (v.fileCopied == false))
{
Log.d (TAG, "onAttachedToWindow(): launching fct");
v.pd.show ();
// may need to check status here to make sure it was cancelled and not something else...
Log.d (TAG, "fct cancel status is " + fct.isCancelled ());
}
super.onResume ();
}
/**
* saves myVars class during rotation
*/
@Override
public Object onRetainNonConfigurationInstance ()
{
Log.d (TAG, "onRetainNonConfigurationInstance(): saving myVars objects");
// close db transfer dialogs if showing so we don't cause a UI error
if (v.pd != null)
if (v.pd.isShowing ())
{
v.taskCancelled = true;
v.pd.cancel ();
}
// save task to myVars so we can use it onRestoreInstanceState
v.fct = fct;
return (v);
}
/*
* restores myVars class after rotation
*/
private void restoreFromObject ()
{
Log.d (TAG, "restoreFromObject(): retrieving myVars object");
v = (myVars) getLastNonConfigurationInstance ();
// initialize myVars object (v) first time program starts
if (v == null)
v = new myVars ();
else
{
Log.d (TAG, "myVars already initialized");
fct = (fileCopyTask) v.fct;
}
}
/**
*
* copy a file from /assets to /data/data/com.blah.blah/databases
*
*/
private class fileCopyTask extends AsyncTask<Integer, Void, Void>
{
// on UI thread here
protected void onPreExecute ()
{
Log.d (TAG, "onPreExecute()" );
// only show this when db has not been copied
// set initDbDone to false prir to call if downloading a new DB
v.pd = new ProgressDialog (HelloAsync.this);
v.pd.setProgressStyle (ProgressDialog.STYLE_HORIZONTAL);
v.pd.setMessage ("Initializing Database");
v.pd.show ();
}
/**
* opens file in assets/ directory and counts the bytes in it so we can have an actual progress bar
*
* @return size
*/
private int getAssetSize ()
{
int size = 0;
try
{
InputStream fin = getBaseContext ().getAssets ().open (DBNAME);
byte [] buffer = new byte [1024];
int length = 0;
while ((length = fin.read (buffer)) > 0)
size += length;
fin.close ();
}
catch (IOException ioe)
{
Log.d (TAG, "fileCopyTask(): asset size failed: ioex :" + ioe);
size = 0;
}
Log.d (TAG, " fileCopyTask(): asset size is " + size);
return (size);
}
@Override
protected Void doInBackground (Integer... params)
{
Log.d (TAG, "doInBackground: +++++++++++++++++++++++++++++++++");
try
{
int inputsize = getAssetSize ();
// this is the input file in the assets directory. We have no idea how big it is.
InputStream fin = getBaseContext ().getAssets ().open (DBNAME);
// this is the destination database file on the android device
File dbFile = getBaseContext ().getDatabasePath (DBNAME);
// check if destination directory exists
String parent = dbFile.getParent ();
// create the desitination directory if it does not exist in /data/data/blah.. (on device)
if (dbFile.exists () == false)
{
boolean result = new File (parent).mkdir ();
Log.d (TAG, " fileCopyTask(): mkdir() result is " + result);
}
// this is the output file in the databases/ subdirectory of the app directory (on device)
OutputStream fout = new FileOutputStream (dbFile);
// transfer bytes from the inputfile to the outputfile
byte [] buffer = new byte [4096];
int length;
int bytesCopied = 0;
while ((length = fin.read (buffer)) > 0)
{
fout.write (buffer, 0, length);
bytesCopied += length;
float b = (float) (bytesCopied * 1.0);
float i = (float) (inputsize * 1.0);
float pctdone = (float) ((b / i) * 100.0);
int pct = (int) (pctdone);
// updating every 4k seems to really slow things down so do it every 5 chunks
if ((pct % 5) == 0)
v.pd.setProgress ((int) pctdone);
}
fout.flush ();
fin.close ();
fout.close ();
}
catch (IOException e)
{
Log.d (TAG, "fileCopyTask(): DB copy blew up. IOE: " + e.getMessage ());
// just in case, attempt to cancel the process in event of cataclysm
this.cancel (true);
e.printStackTrace ();
}
return null;
}
// can use UI thread here
protected void onPostExecute (final Void unused)
{
Log.d (TAG, "fileCopyTask():onPostExecute()");
// set progress bar to 100% when done.
v.pd.setProgress (100);
v.pd.dismiss ();
// set the state flags to show a succesful completion
v.taskCancelled = false;
v.fileCopied = true;
}// onPostExecute()
}// fileCopyTask()
}
///////////////////////////////////////
// myVars.java
//////////////////////////////////////
/**
*
*/
package com.test.helloasync;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.ProgressDialog;
import android.content.DialogInterface;
public class myVars
{
ProgressDialog pd;
boolean taskCancelled = false;
boolean fileCopied = false;
Object fct;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我尝试取消异步任务,但在创建新实例并执行它时遇到了麻烦。之前的 AsyncTask 似乎在轮换过程中仍然存在,并且一旦 NEW 任务被触发就会开始更新 ProgressDialog。
我以为我只是让 android 管理 AsyncTask 本身,因为它似乎想让它在后台运行。所以,我最终所做的只是关闭旋转上的进度对话框,然后在 onResume() 中再次弹出它。 AsyncTask 似乎只是从中断的地方继续。
我将更新上面的代码 - 希望它对其他人有帮助。
I tried cancelling the asynctask, but had trouble when I would create a new instance and execute it. The previous AsyncTask seemed to survive across the rotation and would begin updating the progressDialog as soon as the NEW task was fired-off.
Thought I'd just let android manage the AsyncTask itself since it seems to want to keep it running in the background. So, what I ended up doing was just closing the progressDialog on the rotation, and then popping it up again in onResume(). The AsyncTask seems to just pick up where it left off.
I'll update the code above - hope it helps someone else.
通常,您会设置一个标志(例如
boolean mIsRunning;
),您的doInBackground()
方法会定期检查该标志。如果清除,则停止处理。当您希望取消任务时,请将标志设置为 false。Typically you'd set a flag (e.g.
boolean mIsRunning;
) that yourdoInBackground()
method checks periodically. If it's cleared, stop processing. When you wish to cancel the task, set the flag to false.