从 AsyncTask 滞后 UI 更新 ProgressBar

Posted

技术标签:

【中文标题】从 AsyncTask 滞后 UI 更新 ProgressBar【英文标题】:Updating ProgressBar from AsyncTask lagging UI 【发布时间】:2016-09-26 16:04:05 【问题描述】:

我有一个Fragment 和一个RecyclerView,而RecyclerViewViewHolder 持有一个ProgressBar 以显示下载过程的进度。

public static class ViewHolder extends RecyclerView.ViewHolder 
    ....
    private ProgressBar progressBar = null;
    ....
    public ViewHolder(View itemView) 
        super(itemView);

        ....
        this.progressBar = (ProgressBar) itemView.findViewById(R.id.progessbar);
        ....
    

我为我的Fragment 创建了一个回调:

public interface CallbackItemChanged 
    void onItemChanged(final int position);

如果它被调用,我会这样做:

@Override
public void onItemChanged(final int position) 
    this.adapter.notifyItemChanged(position);

在我的AsyncTask:

protected void onProgressUpdate(Integer... progress) 
    super.onProgressUpdate(progress);

    this.entry.setProgress(progress[0]);
    this.entry.setProgressString(progress[0] + "%");

    this.callbackItemChanged.onItemChanged(this.entry.getPosition());

进度已成功发布,但用户界面非常滞后,但我不知道为什么? onProgressUpdate 是在 ui 线程上运行的不是吗?我觉得应该是这样还是我错了?

如何在更新进度条时让 ui 顺利运行?

编辑

@Override
protected String doInBackground(Void... params) 
    this.inputStream = null;
    this.outputStream = null;
    this.connection = null;

    File file = new File(this.entry.getPath(this.context));
    File parent = file.getParentFile();

    try 
        parent.mkdirs();
        file.createNewFile();

        this.connection = (HttpsURLConnection) new URL(this.entry.getUrl()).openConnection();
        this.connection.connect();

        int fileLength = this.connection.getContentLength();

        this.inputStream = this.connection.getInputStream();
        this.outputStream = new FileOutputStream(file);

        byte data[] = new byte[4096];
        long progress = 0;
        int count;

        while ((count = this.inputStream.read(data)) != -1) 
            if (this.isCancelled() || this.entry.isCancelled()) 
                this.handleClose();
                this.handleDelete();

                return null;
            

            if (fileLength > 0) 
                this.publishProgress((int) ((progress +=count) * 100 / fileLength));
            

            this.outputStream.write(data, 0, count);
        
     catch (Exception e) 
        this.handleDelete();

        return e.toString();
     finally 
        this.handleClose();
    

    return null;

【问题讨论】:

请向我们展示您的整个 AsyncTask 代码。我们需要看看您是如何使用 publishProgress 方法发布进度的。 您可能会用进度更新淹没 UI,但如果没有任何代码表明您发布进度的时间和频率,我们对此无话可说。当您说“用户界面像地狱一样滞后”时,您的意思是用户界面在滚动时出现卡顿,或者可能是其他更量化的东西? 我添加了我的doInBackground 以显示我在哪里使用publishProgress。是的,用户界面在滚动时卡顿。 【参考方案1】:

您在循环中发布进度,因此您的主线程将被调用很多次。

您可以使用简单的 Thread.sleep() 延迟发布进度:

    while ((count = this.inputStream.read(data)) != -1) 
        if (this.isCancelled() || this.entry.isCancelled()) 
            this.handleClose();
            this.handleDelete();

            return null;
        

        // Write the data before publishing the progress
        this.outputStream.write(data, 0, count);

        try
            // Adjust this value. It shouldn't be too small.
            Thread.sleep(100);
        catch (InterruptedException e)
            // Nothing you can do here
        finally 
            if (fileLength > 0) 
                this.publishProgress((int) ((progress +=count) * 100 / fileLength));
            
        
    

或者您只能以 x% 为增量发布:

while ((count = this.inputStream.read(data)) != -1) 
    if (this.isCancelled() || this.entry.isCancelled()) 
        this.handleClose();
        this.handleDelete();

        return null;
    

    // Write the data before publishing the progress
    this.outputStream.write(data, 0, count);

    if (fileLength > 0) 
        currentProgress = ((progress += count) * 100 / fileLength);
        // Publish only on increments of 1%
        if (currentProgress >= previousProgress + 1) 
            this.publishProgress(currentProgress);
            previousProgress = currentProgress;
        

    

【讨论】:

这不会减慢下载过程吗? 是的。另一种方法是仅当您与之前的进度有一定差异时才发布。我会用一个例子来编辑我的答案。

以上是关于从 AsyncTask 滞后 UI 更新 ProgressBar的主要内容,如果未能解决你的问题,请参考以下文章

从另一个 Activity 类执行 Fragment 中的 AsyncTask 并更新其中的 UI

如何从 Android AsyncTask 更改活动 UI?

如何从 android 中的 AsyncTask 获取 UI 参考

Android异步任务AsyncTask

Asynctask 内部类不更新 UI 文本视图

AsyncTask 不从 postexecute 更新 UI