下载大文件时,AsynTask 仍在运行时 ProgrressDialog 冻结

发布于 2024-11-16 18:43:00 字数 7401 浏览 4 评论 0原文

我在实现 AsynTask 和 ProgressDialog 时有勒死行为。当我下载小文件时,一切正常,进度状态从 0% 更新到 100%。但是当我下载较大的文件时,ProgressDialog 上的数字运行到 6% 或 7%,然后不再更新。但 2.3 分钟后,我收到一条消息,表明 asynctask 任务完成了下载过程。

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 message”来自 onPostExecute()。进度对话框停止后 1.2 分钟后出现此消息。如您所见,文件未完全下载。

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

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

I have a strangle behavior when implementing AsynTask and ProgressDialog. When i download small file, everything works fine, the progress status is updated from 0% to 100%. But when i download larger files, the number on ProgressDialog runs to 6 or 7% then it is not updated anymore. But after 2,3 minutes, i receive a messge that asyntask task finished the downloading process.

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;
    }
}

I also add a Log.d message inside onProgressUpdate(), the debug messages show until the progress reaches 6 or 7% then nothing comes out anymore in the console (but the app still works because i don't receive any error messages and the message of Gabrage Collector still shows in the console).

Here is my code

Does anyone have the some problems?

Edited

I changed the buffer size to 1MB as suggestion of DArkO, but it still doesn't work. I think something is wrong with my while loop. I use log.d inside my while loop and have some like this in console:

    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***

The "FINISHED message" comes from onPostExecute(). This message came 1,2 minutes later after the progress dialog had stopped. As you can see the file is not downloaded completely.

I debug the my app with debug tool of eclipse, i can see that the asynctask thread hangs at this function

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半山落雨半山空 2024-11-23 18:43:00

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

考虑使用带有 postDelayed 的处理程序来发送一次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();

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

you are updating it too often it cant display the updates in time and it blocks the ui thread.

consider using a handler with postDelayed to send a publishProgress update maybe every second or 2.

or alternatevely you can increase the buffer size so the loop isnt happening that often, right now you have it on 1024 bytes so maybe half a MB or something like that, but i would still go with the handler method instead. that way your updates and memory consumption doesnt depend on the progress updates.

EDIT:

here is the code i was using for one of my projects to download files. i have tested this with quite large files (between 50 and 100 mb) so it is definitely working. try it out.

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();

you will notice i am using the notification's bar to do the updates instead of a progress bar but the rest should be the same.

装迷糊 2024-11-23 18:43:00

我同意更新发生得太频繁,但我可能不会使用处理程序对象来发布进度,而是使用一些简单的整数数学来编写如下内容。

它使用预先计算的tickSize(实际上是您想要显示进度更新的总大小的百分比),然后使用一些计算量不太大的简单整数除法来跟踪何时显示下一个进度(使用 2 int 而不是 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);
}

// ...

I agree that the updates are occurring too often, but instead of a handler object to publish progress I'd probably write it something like the following with a bit of simple integer maths.

It uses a precalculated tickSize (effectively the percent of the total size you want to show progress updates), and then tracks when to show next progress with a bit of simple integer division that isn't too compute heavy (uses 2 ints instead of a Handler object).

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);
}

// ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文