取消使用ProgressDialog的AsyncTask

时间:2011-05-10 19:00:46

标签: android android-asynctask progressdialog

我有一个AsyncTask,它从DoinBackground启动DatabaseHelper类,它将SQLite数据库从/ assets目录复制到应用程序目录(/ databases)。

在preExecute()中,我启动了progressDialog。随着DB帮助程序类的各个部分的完成,DoinBackground进程会更新progressDialog。

根据我的理解旋转设备时,我需要关闭对话框,取消AsyncTask,然后在轮换完成后再次在onResume()中重新启动。

第一个问题是当我调用AsyncTask.cancel()时,我的onCancel()事件会触发,但AsyncTask会继续运行。我知道因为LogCat在轮换完成后很长时间显示了我的DB Helper的输出。在旋转之后UI是可用的,因为progressDialog已经消失但数据库仍然似乎在复制,所以这不好。

我用Google搜索的一条信息说你无法取消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;
}

2 个答案:

答案 0 :(得分:1)

我尝试取消asynctask,但是当我创建一个新实例并执行它时遇到了麻烦。之前的AsyncTask似乎在轮换期间存活,并且在 NEW 任务被激活后立即开始更新progressDialog。

以为我只是让android管理AsyncTask本身,因为它似乎想让它在后台运行。所以,我最终做的只是在旋转时关闭progressDialog,然后在onResume()中再次弹出它。 AsyncTask似乎只是从它停止的地方开始。

我将更新上面的代码 - 希望它可以帮助其他人。

答案 1 :(得分:0)

通常,您设置了boolean mIsRunning;方法定期检查的标记(例如doInBackground())。如果已清除,请停止处理。如果要取消该任务,请将该标志设置为false。