【问题标题】:canceling an AsyncTask which uses a ProgressDialog取消使用 ProgressDialog 的 AsyncTask
【发布时间】:2011-05-10 19:00:46
【问题描述】:

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

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

据我了解,当设备旋转时,我需要关闭对话框,取消 AsyncTask,然后在旋转完成后在 onResume() 中重新启动两者。

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

我用谷歌搜索的一条信息说你不能取消 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;
}

【问题讨论】:

    标签: android android-asynctask progressdialog


    【解决方案1】:

    我尝试取消异步任务,但在创建新实例并执行它时遇到了麻烦。之前的 AsyncTask 似乎在轮换中存活下来,并且会在 NEW 任务被触发后立即开始更新 progressDialog。

    我以为我会让 android 自己管理 AsyncTask,因为它似乎想让它在后台运行。所以,我最终所做的只是关闭轮换的progressDialog,然后在onResume() 中再次弹出它。 AsyncTask 似乎只是从中断的地方开始。

    我将更新上面的代码 - 希望对其他人有所帮助。

    【讨论】:

      【解决方案2】:

      通常您会设置一个标志(例如boolean mIsRunning;),您的doInBackground() 方法会定期检查该标志。如果已清除,则停止处理。当您希望取消任务时,请将标志设置为 false。

      【讨论】:

      • 我已经玩了几天了。我可以取消 AsyncTask 和 progressDialog,但是如果您旋转设备几次,进度条会开始来回跳跃。我认为正在发生的是同时运行多个 AsyncTask。如何防止 AsyncTask 的多个副本在轮换期间生成?我已经用一个完整的工作示例编辑了上面的代码。
      猜你喜欢
      • 1970-01-01
      • 2014-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2015-04-15
      • 1970-01-01
      相关资源
      最近更新 更多