我有一个AsyncTask从DoinBackground启动DatabaseHelper类,它将SQLite数据库从/ assets目录复制到应用程序目录(/ databases)。取消使用ProgressDialog的AsyncTask
在preExecute()中,我启动了一个progressDialog。随着DB帮助程序类的各个部分完成,DoinBackground进程将更新progressDialog。
当设备旋转时,据我了解,我需要关闭对话框,取消AsyncTask,然后在旋转完成后再次在onResume()中重新启动。
第一个问题是当我调用AsyncTask.cancel()时,我的onCancel()事件触发,但AsyncTask继续运行。我知道,因为LogCat在旋转完成后很久才显示来自我的数据库帮助器的输出。 UI在旋转后可用,因为progressDialog消失了,但数据库似乎仍在复制,所以这不太好。
一条信息我googled说你不能取消一个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;
}
我已经玩了几天了。我可以取消AsyncTask和progressDialog,但是如果您旋转设备几次,进度条开始疯狂地来回跳动。我认为发生的事情是同时运行多个AsyncTasks。如何在循环过程中保持AsyncTask的多个副本不会产卵?我用一个完整的工作示例编辑了上面的代码。 – wufoo 2011-05-12 13:16:45