【问题标题】:Unable to update Circular ProgressBar while downloading file下载文件时无法更新循环进度条
【发布时间】:2020-12-18 21:26:22
【问题描述】:

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

我说的是 RecyclerView Adapter 中的进度条。

下面是我的改造电话, 当点击 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 调用时,我才会遇到这个问题。如果我在 Asynctask 中使用普通 HTTPUrlConnection 进行 API 调用,则进度加载工作正常。

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

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

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

下面我也试过了,

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% 下载完成。在未反映进度百分比更新之前。

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

提前致谢。

【问题讨论】:

    标签: java android download retrofit2 android-progressbar


    【解决方案1】:

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

    希望你能明白。

    【讨论】:

    • 我的 recylerview 项目中有进度条。我在 recycler 适配器中分配进度。这就是为什么我不知道在这种情况下如何使用监听器。
    • 您也可以对适配器进行类似的实现。适配器可以实现UIUpdater 并且可以覆盖其中的相同方法。如果有帮助,您可能需要修改接口以获取额外的位置参数以使用 RecyclerView
    • 感谢您的回答 :-)。但无法解决我的 pbm。我正在 Retrofit2 响应中执行 Asynctas。可能是这样想的问题。
    • 调用了覆盖的方法,但未反映在 UI 中。所以尝试了不同的东西,比如使用处理程序,runOnUIthread。但没有任何帮助
    【解决方案2】:

    您的问题不在于您使用的是 RecyclerView,您的问题是您没有正确使用它。

    这既不是适配器,也不是视图(RecyclerView),甚至不是 ViewHolder 的责任来确定这里的任何内容。

    • 我假设您有一个带有进度条的 ViewHolder 类型。
    • 我假设您有一个 ListAdapter&lt;T, K&gt; 和相应的 DiffUtilCallback 实现。
    • 我假设进度是在其他地方处理/报告的(在您的存储库中,通过 viewModel 或 Presenter,通过 useCase、Interactor,甚至是普通的 ViewModel)。
    • 您的适配器现在只能等待。
    • 当进度更新时,您准备要显示的列表,以便更新进度已更改的项目。
    • 然后,您将此新列表(以及新进度)提交给您的适配器。
    • 您的 DiffUtil 会计算这个(它也可以是异步的!)
    • 您的 RecyclerView 已神奇更新,无需破解任何内容。

    如果其中任何一个不正确,请执行必要的调整,以便正确测试您的代码,并更好地分离每段代码的关注点。

    这样想,假设您拥有所有这些,并且您更新的“进度值”中有一个小错误。哪个更容易,在 ViewModel 或 UseCase/interactor 中将 Retrofit 值转换为 &lt;Thing&gt; 的小函数中搜索,或者在你的意大利面条式回调代码中搜索?

    【讨论】:

    • 感谢您的回答。在我的情况下,我正在一个项目的 onclick 监听器中进行改造调用。我正在 writeResponseBodyToDisk 方法的 while 循环内更新进度条。但不会反映在 UI 中。
    • 你如何告诉你的适配器数据改变了?
    • 感谢您的帮助 :-) 我找到了解决方案。稍后会在这里发布。
    • 不用担心,很高兴知道您找到了解决方案。如果您有兴趣,我修改了一个不相关的示例,以在“假”慢操作期间显示进度条。您可以在此处查看 master(或具有 viewMODels 的主题分支)与此新分支之间的“差异”github.com/Gryzor/CheckBoxCounter/compare/… 如果您运行它,您会注意到您可以点击复选框,并且一旦它们是“就会出现进度”完成”它们再次更新以显示正确的 UI。 “计数器”无关紧要。 :) 祝你好运。
    【解决方案3】:

    感谢大家帮助我!!

    这个答案帮助我更新了 Circular ProgressBar https://stackoverflow.com/a/42119419/11630822

    public static Retrofit getDownloadRetrofit(String baseUrl, DownloadVoicemailListener listener) {
    
            return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(getOkHttpDownloadClientBuilder(listener).build())
                    .build();
    
        }
    
        private static OkHttpClient.Builder getOkHttpDownloadClientBuilder(DownloadVoicemailListener listener) {
    
            OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder().connectionSpecs(Collections.singletonList(getConnectionSpec()));
    
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            if (!releaseMode) {
    
                logging.level(HttpLoggingInterceptor.Level.BODY);
                httpClientBuilder.addInterceptor(logging);
            }
    
            httpClientBuilder.connectTimeout(20, TimeUnit.SECONDS);
            httpClientBuilder.writeTimeout(0, TimeUnit.SECONDS);
            httpClientBuilder.readTimeout(5, TimeUnit.MINUTES);
    
            httpClientBuilder.addInterceptor(new Interceptor() {
                @NotNull
                @Override
                public Response intercept(@NotNull Interceptor.Chain chain) throws IOException {
                    if (listener == null) return chain.proceed(chain.request());
    
                    Response originalResponse = chain.proceed(chain.request());
                    return originalResponse.newBuilder()
                            .body(new ProgressResponseBody(originalResponse.body(), listener))
                            .build();
                }
            });
    
            return httpClientBuilder;
        }
    

    在 Progressbody 类中,

    
    public class ProgressResponseBody extends ResponseBody {
        private final String TAG=ProgressResponseBody.class.getSimpleName();
        private  ResponseBody responseBody;
        private BufferedSource bufferedSource;
    
        public ProgressResponseBody(ResponseBody responseBody, DownloadVoicemailListener progressListener) {
            this.responseBody = responseBody;
            progressListener.readFile(responseBody);
        }
    
        @Override public MediaType contentType() {
            return responseBody.contentType();
        }
    
        @Override public long contentLength() {
            return responseBody.contentLength();
        }
    
        @NotNull
        @Override public BufferedSource source() {
    
            if (bufferedSource == null) {
                bufferedSource = Okio.buffer(source(responseBody.source()));
            }
            return bufferedSource;
    
        }
    
        private Source source(Source source) {
            return new ForwardingSource(source) {
                long totalBytesRead = 0L;
    
                @Override public long read(Buffer sink, long byteCount) throws IOException {
    
                    return byteCount;
                }
            };
        }
    
    
    }
    
    
    
    @Override
        public void readFile(ResponseBody responseBody) {
            boolean result = writeResponseBodyToDisk(responseBody);
           
        }
    
    private boolean writeResponseBodyToDisk(ResponseBody body) {
            try {
    
                InputStream inputStream = null;
                OutputStream outputStream = null;
    
                try {
                    long fileSizeDownloaded = 0;
                    AndroidLogger.log(5, TAG, "File path" + filePath);
                    AndroidLogger.log(5, TAG, "File size" + fileSize);
                    long lengthOfFile = Long.parseLong(String.format("%.0f", Double.parseDouble(this.fileSize))) * 1024;
                    AndroidLogger.log(5, TAG, "filesize" + fileSize + "length of file" + lengthOfFile);
                    inputStream = body.byteStream();
                    outputStream = new FileOutputStream(this.filePath);
    
                    byte[] data = new byte[4096];
                    long total = 0;
                    int count;
                    while ((count = inputStream.read(data)) != -1) {
                        if (cancelDownload) {
                            AndroidLogger.log(5,TAG,"Cancel download clicked");
                            inputStream.close();
                            return false;
                        }
                        total += count;
                        outputStream.write(data, 0, count);
                        fileSizeDownloaded += count;
                        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);
                            ((Activity) context).runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    circularProgressBar.setProgress(progress);
                                }
                            });
    
                        }
                    }
                    AndroidLogger.log(5, TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
                    outputStream.flush();
                    return true;
                } catch (IOException e) {
                    e.printStackTrace();
                    return false;
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }
    
                    if (outputStream != null) {
                        outputStream.close();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-04
      • 1970-01-01
      • 2016-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多