使用AsyncTask冻结用户界面线程

7
我正在尝试使用AsyncTask下载文件,并通过通知进度条显示下载进度。一切都很顺利(在后台线程中下载),但每当我添加代码通过publishProgress()更新进度条时,它会冻结整个手机,直到下载完成并显示“下载完成”通知为止。
我完全不知道为什么会这样,但我认为可能是我使用的publishProgress((downloadedSize / totalSize) * 100)导致的?
无论如何,以下是DownloadDataTask:
    protected String doInBackground(RomDataSet... params) {
        try {

            // Download file here, all good

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

                publishProgress((downloadedSize / totalSize) * 100);
            }
            //close the output stream when done
            fileOutput.close();

        //catch some possible errors...
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPreExecute() {
        Intent intent = new Intent(ListActivity.this, ListActivity.class);
        pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);

        // configure the notification
        notification = new Notification(R.drawable.ic_stat_rom, "Downloading Rom via RomGet", System
                .currentTimeMillis());
        notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
        notification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.layout_download);
        notification.contentIntent = pendingIntent;
        notification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon_rom);
        notification.contentView.setTextViewText(R.id.download_description, "Downloading");
        notification.contentView.setProgressBar(R.id.download_progress, 100, 0, false);

        getApplicationContext();
        notificationManager = (NotificationManager) getApplicationContext().getSystemService(
                Context.NOTIFICATION_SERVICE);

        notificationManager.notify(43, notification);
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        //notification.contentView.setProgressBar(R.id.download_progress, 100, Math.round(progress[0]), false);
        notification.contentView.setTextViewText(R.id.download_description, Integer.toString(progress[0]));
        // inform the progress bar of updates in progress
        notificationManager.notify(43, notification);
    }

    @Override
    protected void onPostExecute(String result) {
        notification.contentView.setProgressBar(R.id.download_progress, 100, 100, false);
        notification.contentView.setTextViewText(R.id.download_description, "Done");
        notificationManager.notify(43, notification);
    }

我真的被这个问题卡住了 - 任何帮助都将不胜感激。谢谢。

    @Override
    protected void onProgressUpdate(Integer... progress) {
        if ((progress[0] - lastSize) > 5000) {
            lastSize = progress[0];
            notification.contentView.setProgressBar(R.id.download_progress, totalSize, progress[0], false);
            //notification.contentView.setTextViewText(R.id.download_description, Integer.toString(progress[0]));
            // inform the progress bar of updates in progress
            notificationManager.notify(43, notification);
        }
    }

也许你调用 publishProgress() 的频率太高了?把 (downloadedSize / totalSize) * 100 的最后一个值保存在一个整数中,只有当它与上一个值不同时才调用 publishProgress(),如何? - dmon
很好的想法,除了现在它不会冻结,而是给我以下错误(我已将其编辑到主帖中) - jsw
你一定需要包含剩余的代码。没有它,我们无法知道为什么是 null 以及原因。解决这个问题所需做的就是仔细跟踪堆栈并记录正在发生的事情。 - Thomas Dignan
抱歉,我已经修复了我发布的那个错误。然而,即使我尝试在上次更新大小和当前更新大小之间的差异为5000时进行更新,它仍会冻结手机(我认为是因为它正在更新太多)。我已经提供了如何更新的示例。我不明白为什么这是个问题,我需要减少更新频率吗? - jsw
我认为它不在同一个线程上,因为你之前也提到了“后台线程”,但是我也是人,可能会错。Bill似乎提出了一个好建议,也许所有的进度更新都太多了。 - Austin
显示剩余2条评论
1个回答

9

使用取模运算符仅在下载进度达到 5、10 或 20% 时进行更新。在下载过程中不断调用 onProgressUpdate()。

if (downloadedSize % (totalSize / 20) == 0) { // 20 updates every 5%, 10 every 10% and 5 every 20%, etc.
    publishProgress((downloadedSize / totalSize) * 100);
}

2
干杯!虽然你的代码在我的情况下实际上并没有起作用,但我通过检查自上次通知更新以来的“downloadedSize”是否大于“totalSize”的5%来将其调整为我的publishProgress()。现在运行得很好,干杯! - jsw
1
你指引了我正确的方向!非常感谢!请吃块饼干。 - Mariano Zorrilla
@jsw,能否请您提供你的代码给我呢? - B.shruti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接