首页 > 解决方案 > 下载文件时无法更新循环进度条

问题描述

我使用 Retrofit2 进行文件下载。我无法使用进度值更新 ProgressBar。我得到了进步值。所以不存在问题。当我将进度值设置为未反映在 UI 中的进度条时。

我说的是 RecyclerView 适配器中存在的进度条。

下面是我的改造调用,当单击 RecyclerView 中的项目时,将调用此方法。

 private void downloadFileFromServer(String otpapi, String userName, String password, String code, String vmFileName, String filePath, String vmFileSize, int position, CircularProgressBar circularProgress) {
      
        GetDataForApiCall getDataForApiCall= RetrofitInstance.getRetrofit(url,otpapi,context).create(GetDataForApiCall.class);
      
        Call<ResponseBody> downloadVoicemail=  getDataForApiCall.downloadVoiceMail(userName,password,code,vmFileName);
        this.circularProgressBar=circularProgress;
        this.circularProgressBar.setIndeterminate(false);
        this.circularProgressBar.setProgress(0);
        this.circularProgressBar.setMax(100);
        this.circularProgressBar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AndroidLogger.log(5,"onClick","circularProgressBar onClick executed!!");
                Toast.makeText(context,"cancel clicked",Toast.LENGTH_LONG).show();
                cancelDownload = true;
            }
        });
        downloadVoicemail.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

boolean downloadResult = writeResponseBodyToDisk(response.body(),vmFileSize,filePath);
                if(downloadResult) {
                    Toast.makeText(context, "File downloaded", Toast.LENGTH_SHORT).show();
                    updateVoiceMailFilePath(position, filePath);
                    updateViews(position);
                }else {
                    deleteVoiceMailFileFromLocalSystem(filePath);
                    updateViews(position);
                }

            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {

            }
        });
    }

private boolean writeResponseBodyToDisk( ResponseBody body, String fileSize, String filePath) {
        try {
            InputStream inputStream = null;
            OutputStream outputStream = null;

            try {
                byte[] fileReader = new byte[8192];
                //long fileSize = body.contentLength();
                long fileSizeDownloaded = 0;
                long lengthOfFile = Long.parseLong( String.format( "%.0f",Double.parseDouble(fileSize )) )  * 1024;
                AndroidLogger.log(5,TAG,"filesize"+fileSize + "length of file"+lengthOfFile);
                inputStream = body.byteStream();
                outputStream = new FileOutputStream(filePath);

                while (true) {
                    int read = inputStream.read(fileReader);

                    if(cancelDownload){
                        inputStream.close();
                        return false;
                    }
                    if (read == -1) {
                        AndroidLogger.log(5,TAG,"-1 value so break");
                        break;
                    }
                    outputStream.write(fileReader, 0, read);

                    fileSizeDownloaded += read;
                    if(lengthOfFile >0) {
                    AndroidLogger.log(5,TAG,"FileSize downloaded"+ fileSizeDownloaded);
                        int progress = (int) (fileSizeDownloaded * 100 / lengthOfFile);
                    AndroidLogger.log(5,TAG,"Length of  file"+ lengthOfFile);
                    AndroidLogger.log(5,TAG,"Progress"+ progress);
                    this.circularProgressBar.setProgress(progress);
                        update(progress);
                    }
                    AndroidLogger.log(5,TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
                }

                outputStream.flush();

                return true;
            } catch (IOException e) {
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }

                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (IOException e) {
            return false;
        }
    }

我也尝试过 Listener 来更新值,因为改造调用是在其他线程上完成的。因此,对于更新 UI,我使用了没有帮助的侦听器。

我正在使用 Retrofit2 进行 API 调用。因此,为了在所有活动中更新 UI,我使用了接口侦听器。这适用于所有活动。但是当我在 RecyclerView Adapter 类中尝试同样的事情时,无法更新进度条。在调用 api 之前,我已将进度条设置为 0,最大值设置为 100。

下面的情况下工作正常,

API 调用前的循环进度条设置为零

下载完成后的圆形进度条将变为勾号

下面不工作,

带有加载指示的圆形进度条

注意:只有在使用 Retrofit2 进行 API 调用时,我才会遇到这个问题。如果我使用普通的 HTTPUrlConnection 在 Asynctask 中进行 API 调用,那么进度加载工作正常。

我已经通过下面的代码检查了进度更新是否发生在主线程上,

if(Looper.myLooper() == Looper.getMainLooper()) {
circularProgressBar.setProgress(progress);
}

满足上述条件。尽管进度条没有更新。

我也在下面尝试过,

Handler mainHandler = new Handler(Looper.getMainLooper());

                        Runnable myRunnable = new Runnable() {
                            @Override
                            public void run() {
                                AndroidLogger.log(5,TAG,"Running on UI thread");
                                circularProgressBar.setProgress(progress);
                            } 
                        };
                        mainHandler.post(myRunnable);


                    }

我已经把它放在 writeResponseBodyToDisk 方法里面,在 while 循环里面,但是它只被调用了两次并且进度条没有更新。

我评论了进度条加载将更改为刻度线的部分。之后,当我尝试下载时,一旦下载完成,就可以在进度条中看到 100% 下载完成。未反映进度百分比更新之前。

请任何人帮我解决这个问题。

提前致谢。

标签: javaandroiddownloadretrofit2android-progressbar

解决方案


UI 更新需要在 UI 线程中进行。从后台线程(即AsyncTask)设置颜色实际上不会更新 UI,因为这不会发生在 UI 线程中。有几种方法可以更新 UI 中的进度颜色。我建议使用一个接口和一个回调函数,以便您可以调用该回调函数来从实现它的片段的活动中更新 UI。这是一个澄清。

让我们先声明一个接口。

public interface UIUpdater {
    void updateUI(int progressValue);
}

现在在要更新 UI 的 Activity 或 Fragment 中实现此接口。

public class MainActivity extends Activity implements UIUpdater {

    @Override
    public void updateUI(int progressValue) {
        // Do the UI update here. 
        circularProgress.setProgress(progressValue); // Or anything else that you want to do. 
    }
}

您想修改初始化您的构造函数AsyncTask以将UIUpdater类作为参数传递。

public class YourAsyncTask extends AsyncTask<String, Void, String> {

    UIUpdater listener;

    public YourAsyncTask(UIUpdater listener) {
        this.listener = listener;
    }
}

这样您就可以AsyncTask使用以下内容从活动/片段中调用。

YourAsyncTask myTask = new YourAsyncTask(this); // When you are passing this from activity, you are implicitly passing the interface that it implemented. 
myTask.execute(); 

现在从异步任务中,当您发布进度时,调用侦听器函数以使用您的活动/片段的 UI 线程更新 UI。

protected void onProgressUpdate(Integer... values) {
    super.onProgressUpdate(values);

    try {
       listener.updateUI(values[0]);
    }
} 

希望你能明白。


推荐阅读