【问题标题】:ProgrressDialog frezees while AsynTask is still running when download large file下载大文件时,当 AsyncTask 仍在运行时,ProgressDialog 冻结
【发布时间】:2011-06-21 19:26:25
【问题描述】:

在实现 AsynTask 和 ProgressDialog 时,我有一种扼杀行为。当我下载小文件时,一切正常,进度状态从 0% 更新到 100%。但是当我下载更大的文件时,ProgressDialog 上的数字会运行到 6% 或 7%,然后就不再更新了。但是 2,3 分钟后,我收到一条消息,说 asyntask 任务完成了下载过程。

public class DownloadHelper extends AsyncTask<String, Integer, Long> implements DialogInterface.OnDismissListener{

    private volatile boolean running = true;

    private PhonegapActivity _ctx = null;
    private ProgressDialog _progressDialog = null;
    private String _title = null;
    private File _root = null;
    private File _destination = null;
    private DatabaseHelper _dbHelper = null;

    private Cursor _cursorMedia = null;

    public DownloadHelper(String title, File root, File destination, DatabaseHelper dbHelper, PhonegapActivity ctx){
        _title = title;
        _ctx = ctx;
        _root = root;
        _destination = destination;
        _dbHelper = dbHelper;
    }

    @Override
    protected void onPreExecute() {
        if (_progressDialog != null)
        {
            _progressDialog.dismiss();
            _progressDialog = null;
        }
        _progressDialog = new ProgressDialog(_ctx);
        _progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        _progressDialog.setTitle("Downloading");
        _progressDialog.setMessage(_title);
        _progressDialog.setCancelable(true);
        _progressDialog.setMax(100);
        _progressDialog.setProgress(0);
        /*_progressDialog.setOnCancelListener(
            new DialogInterface.OnCancelListener() { 
                public void onCancel(DialogInterface dialog) {
                    _progressDialog = null;
                    running = false;
                }
        });
        _progressDialog.setOnDismissListener(
            new DialogInterface.OnDismissListener() {
                public void onDismiss(DialogInterface dialog) {
                    Log.d("DownloadHelper", "canceled inside listener");
                    _progressDialog = null;
                    running = false;
                }
            }
        );*/
        _progressDialog.show();

        running = true;
     }

    @Override
    protected Long doInBackground(String... sUrl) {
        try {
            Log.d("DownloadHelper", "Start download from url " + sUrl[0]);
            long total = 0;
            total = _download(sUrl[0], _destination);

            return total;
        } catch (Exception ex2) {
            ex2.printStackTrace();
            Log.d("DownloadHelper", "Failed to download test file from " + sUrl[0] + " to " + _destination.getAbsolutePath().toString());
            _closeProgressDialog();
        }
        return null;
    }

    protected void onCancelled(Long result) {
        Log.d("DownloadHelper", "CANCELLED result = " + result);
        _closeProgressDialog();
    }

    protected void onProgressUpdate(Integer... progress) {
        if (_progressDialog != null && running)
        {
            Log.d("DownloadHelper", "UPDATED progess = " + progress[0]);
            _progressDialog.setProgress(progress[0]);
        }
        else //cancel the task
        {
            Log.d("DownloadHelper", "onProgressUpdate cancelled");
            cancel(true);
        }
    }

    protected void onPostExecute(Long result) {
        Log.d("DownloadHelper", "FINISHED result = " + result);

        // Close the ProgressDialog
        _closeProgressDialog();
        running = false;

        if (result != null) //OK
        {
            _showAlertDialog("Test has been downloaded successfully.", "Message", "OK");        
        }
        else // error
        {
            _showAlertDialog("Can not download the test. Please try again later.", "Error", "OK");      
        }

    }

    @Override
    protected void onCancelled() {
        running = false;
    }    

    public void onDismiss(DialogInterface dialog) {
        Log.d("DownloadHelper", "Cancelled");
        this.cancel(true);
    }


    protected void _closeProgressDialog(){
        if (_progressDialog != null)
        {
            _progressDialog.dismiss();
            _progressDialog = null;
        }
    }

    protected void _showAlertDialog(final String message, final String title, final String buttonLabel){
        AlertDialog.Builder dlg = new AlertDialog.Builder(_ctx);
        dlg.setMessage(message);
        dlg.setTitle(title);
        dlg.setCancelable(false);
        dlg.setPositiveButton(buttonLabel,
                new AlertDialog.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        dlg.create();
        dlg.show();     
    }

    protected Cursor _checkMedia() {

        _dbHelper.openDatabase(_destination.getAbsolutePath());
        Log.d("DownloadHelper", "Database is opened");

        String[] columns = {"type, size, location, location_id, url"};
        Cursor cursor = _dbHelper.get("media", columns);

        _dbHelper.closeDatabase();
        _dbHelper.close();
        _dbHelper = null;

        return cursor;
    }

    protected long _download(String sUrl, File destination) throws IOException {
        URL url = new URL(sUrl);
        URLConnection conexion = url.openConnection();
        conexion.connect();
        // this will be useful so that you can show a tipical 0-100% progress bar
        int lenghthOfFile = conexion.getContentLength();
        Log.d("DownloadHelper", "length of File = " + lenghthOfFile);

        // downlod the file
        InputStream input = new BufferedInputStream(url.openStream());
        OutputStream output = new FileOutputStream(destination);

        byte data[] = new byte[1024];

        long total = 0;
        int count;

        // Reset the progress
        _progressDialog.setProgress(0);

        // Start downloading main test file
        while ((count = input.read(data)) != -1 && running) {
            total += count;
            Log.d("DownloadHelper", "total = " + total);
            // publishing the progress....
            this.publishProgress((int)(total*100/lenghthOfFile));
            output.write(data, 0, count);
        }

        if (running == false)
        {
            this.cancel(true);
        }

        output.flush();
        output.close();
        input.close();

        return total;
    }
}

我还在 onProgressUpdate() 中添加了一条 Log.d 消息,调试消息显示直到进度达到 6% 或 7%,然后控制台中不再出现任何内容(但该应用程序仍然有效,因为我没有收到任何错误消息和Gabrage Collector的消息仍然显示在控制台中)。

这是我的代码

有人有问题吗?

已编辑

根据 DArkO 的建议,我将缓冲区大小更改为 1MB,但它仍然不起作用。我认为我的 while 循环有问题。我在我的 while 循环中使用 log.d 并在控制台中有一些这样的:

    D/DownloadHelper( 1666): length = **3763782**; total = 77356; percent = 2; save_percent = 0
    D/DownloadHelper( 1666): UPDATED progess = 2
    D/DownloadHelper( 1666): length = 3763782; total = 230320; percent = 6; save_percent = 0
    D/DownloadHelper( 1666): UPDATED progess = 6
    D/dalvikvm( 1666): GC freed 10241 objects / 1087168 bytes in 88ms
    *D/DownloadHelper( 1666): FINISHED result = **230320***

“FINISHED 消息”来自 onPostExecute()。此消息在进度对话框停止后 1.2 分钟后出现。如您所见,该文件未完全下载。

我用eclipse的调试工具调试我的应用程序,我可以看到asynctask线程在这个函数处挂起

OSNetworkSystem.receiveStreamImpl(FileDescriptor, byte[], int, int, int) line: not available [native method]    

【问题讨论】:

    标签: android cordova android-asynctask freeze progressdialog


    【解决方案1】:

    你更新太频繁了,它无法及时显示更新,它阻塞了 ui 线程。

    考虑使用带有 postDelayed 的处理程序以每秒或 2 次发送 publishProgress 更新。

    或者你可以增加缓冲区大小,这样循环就不会经常发生,现在你有它在 1024 字节上,所以可能是半 MB 或类似的东西,但我仍然会使用处理程序方法。这样你的更新和内存消耗不依赖于进度更新。

    编辑:

    这是我用于我的一个项目下载文件的代码。我已经用相当大的文件(50 到 100 mb 之间)对此进行了测试,所以它肯定可以工作。试试看。

    try {
    
            // this is the file to be downloaded
            final URL url = new URL(Url); // set the download URL, a url that
            // points to a file on the internet
            // create the new connection
            final HttpURLConnection urlConnection = (HttpURLConnection) url
                    .openConnection();
    
            // set up some things on the connection and connect!
            urlConnection.setRequestMethod("GET");
            urlConnection.setDoOutput(true);
            urlConnection.setConnectTimeout(4500); // Connection timeout in
            // miliseconds
    
            urlConnection.connect();
            Log.i(TAG, "Connected");
            // set the path where we want to save the file
            // in this case on root directory of the
            // sd card.
            File directory = new File(Environment.getExternalStorageDirectory().getAbsoluteFile()+MUSIC_VIDEOS_PATH+artist);
            // create a new file, specifying the path, and the filename
            // which we want to save the file as.
    
            directory.mkdirs(); // If we want to save the file in another
            // directory on the SD card
            // we need to make the directories if they dont exist.
    
            // you can download to any type of file ex: (image), (text file),
            // (audio file)
            Log.i(TAG, "File Name:" + filename);
            final File file = new File(directory, filename);
            if (file.createNewFile()) {
                file.createNewFile();
            }
    
            // this will be used to write the downloaded data into the file we
            // created
            final FileOutputStream fileOutput = new FileOutputStream(file);
    
            // this will be used in reading the data from the internet
            final InputStream inputStream = urlConnection.getInputStream();
            // this is the total size of the file
            final int totalSize = urlConnection.getContentLength();
            // variable to store total downloaded bytes
            int downloadedSize = 0;
    
            // a buffer
            final byte[] buffer = new byte[1024*1024];
            int bufferLength = 0; // used to store a temporary size of the
            // buffer
            int lastProgress = 0, progress = 0;
            // now, read through the input buffer and write the contents to the
            // file
            while ((bufferLength = inputStream.read(buffer)) > 0) {
                // add the data in the buffer to the file in the file output
                // stream (the file on the sd card
                fileOutput.write(buffer, 0, bufferLength);
                // add up the size so we know how much is downloaded
                downloadedSize += bufferLength;
                // this is where you would do something to report the prgress,
                // like this maybe
                // Log.i("Progress:","downloadedSize:"+String.valueOf((int)((downloadedSize/(double)totalSize)*100))+" %  totalSize:"+
                // totalSize) ;
                progress = (int) ((downloadedSize / (double) totalSize) * 100);
                if (progress != lastProgress && progress % 10 == 0) {
                    notification.contentView.setProgressBar(R.id.ProgressBar01,
                            100, progress, false);
                    // inform the progress bar of updates in progress
                    nm.notify(id, notification);
                    Log.i(TAG, String.valueOf(progress));
                }
                lastProgress = progress;
            }
            // close the output stream when done
            fileOutput.flush();
            fileOutput.close();
    

    您会注意到我正在使用通知栏而不是进度条进行更新,但其余部分应该相同。

    【讨论】:

    • 我同意使用 1024 字节的缓冲区也会非常低效。我建议根据正在下载的文件的大小进行分配 - 使用 lenghthOfFile 并分配一个可能占总数的 20% 或 10% 的缓冲区。这也会减慢进度更新。
    • 或者,你知道,每次percentage(一个新变量)增加1时才发布更新。
    • 首先关于百分比:这意味着他还需要计算新的百分比,比较并存储它——发布进度更新,这也是非常低效的。关于缓冲区大小,我的意见仍然是您需要使用固定大小而不是动态调整文件大小,因为考虑到较大的文件(例如 100 mb 意味着分配 20 mb 缓冲区,这肯定是内存不足崩溃,50 mb 是 10mb 缓冲区,这可能也是内存不足的情况。我的经验告诉我,0.5 mb 到 1 mb 是一个很好的缓冲区大小。昂贵的应用程序甚至更少
    • 我已经编辑了我的回复,为您提供我之前用于下载较大文件的示例。看看吧。
    【解决方案2】:

    我同意更新发生得太频繁了,但是我可能会用一些简单的整数数学来代替一个处理程序对象来发布进度,而不是像下面这样写。

    它使用预先计算的 tickSize(实际上是您想要显示进度更新的总大小的百分比),然后使用计算量不大的简单整数除法跟踪何时显示下一个进度(使用 2 @ 987654321@s 而不是 Handler 对象)。

    int lengthOfFile = conexion.getContentLength(); // note spelling :)
    int tickSize = 2 * lengthOfFile / 100; // adjust to how often you want to update progress, this is 2%
    int nextProgress = tickSize;
    
    // ...
    
    while ((count = input.read(data)) != -1 && running) {
        total += count;
        if (total >= nextProgress) {
            nextProgress = (total / tickSize + 1) * tickSize;
            this.publishProgress((int)(total*100/lengthOfFile));
        }
        output.write(data, 0, count);
    }
    
    // ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多