[英]Unable to update Circular ProgressBar while downloading file
I have used Retrofit2 for file download.我使用 Retrofit2 进行文件下载。 I am not able to update ProgressBar with progress value.
我无法使用进度值更新 ProgressBar。 I got progress value.
我得到了进步值。 So there is not issue.
所以没有问题。 When I set the progress value to progress bar not reflected in UI.
当我将进度值设置为 UI 中未反映的进度条时。
I am talking about Progress Bar which is present inside RecyclerView Adapter.我说的是 RecyclerView Adapter 中存在的进度条。
Below is my retrofit call, And this method will be called when clicking a item inside 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;
}
}
Also I have tried Listener to update value because retrofit call done on some other thread.我也尝试过 Listener 来更新值,因为在其他线程上完成了改造调用。 So for update UI I have used listener which is not helped.
因此,对于更新 UI,我使用了没有帮助的侦听器。
I am using Retrofit2 for making API calls.我正在使用 Retrofit2 进行 API 调用。 So for updating UI in all Activities I had used interface listeners.
因此,为了更新所有活动中的 UI,我使用了界面侦听器。 This works perfect for all Activities.
这适用于所有活动。 But when I tried the same thing in RecyclerView Adapter class, not able to update progress bar.
但是当我在 RecyclerView Adapter 类中尝试同样的事情时,无法更新进度条。 Before calling api I had set Progress bar to 0 and max to 100.
在调用 api 之前,我已将进度条设置为 0,最大设置为 100。
Below case Works fine,下面的案例工作正常,
Circular ProgressBar before API call set to Zero API 调用前的圆形 ProgressBar 设置为零
Circular ProgressBar after download Completed will change to a tick mark下载完成后的圆形进度条会变成一个勾号
Below is not Working,下面不工作,
Circular ProgressBar with indication of loading带有加载指示的圆形进度条
NOTE: I am facing this issue only when used Retrofit2 to make API call.注意:我只有在使用 Retrofit2 进行 API 调用时才会遇到这个问题。 If I used normal HTTPUrlConnection for making API call inside a Asynctask, then progress loading working fine.
如果我使用普通的 HTTPUrlConnection 在 Asynctask 内进行 API 调用,那么进度加载工作正常。
I have checked whether the progress updation is occurs on main thread or not by below code,我已经通过下面的代码检查了进度更新是否发生在主线程上,
if(Looper.myLooper() == Looper.getMainLooper()) {
circularProgressBar.setProgress(progress);
}
The above if condition is satisfied.以上条件满足。 Eventhough Progress bar not updated.
尽管进度条未更新。
Also I have tried below,我也试过下面,
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);
}
I have placed this inside writeResponseBodyToDisk method, inside while loop, But it was called only two times and progress bar not updated.我已经把它放在 writeResponseBodyToDisk 方法中,在 while 循环中,但它只被调用了两次并且进度条没有更新。
I commented the part, where the progress bar loading will change to a tick mark.我评论了进度条加载将更改为刻度线的部分。 After that when I tried download,once download completed able to see 100 percent download completed in progress bar.
之后,当我尝试下载时,下载完成后可以在进度条中看到 100% 下载完成。 Before progress percent updation not reflected.
未反映进度百分比更新之前。
Please Anybody help me out to solve this issue.请任何人帮我解决这个问题。
Thanks in advance.提前致谢。
The UI updates are needed to be happened in the UI thread. UI 更新需要在 UI 线程中进行。 Setting the color from a background thread (ie
AsyncTask
) will not actually update the UI as this is not happening in the UI thread.从后台线程(即
AsyncTask
)设置颜色实际上不会更新 UI,因为这不会发生在 UI 线程中。 There are several ways to update the progress color in the UI.有多种方法可以更新 UI 中的进度颜色。 I would recommend having an interface along with a callback function so that you can invoke that callback function to update the UI from the activity of fragment that implemented it.
我建议有一个接口和一个回调函数,这样你就可以调用该回调函数来从实现它的片段的活动更新 UI。 Here's a clarification.
这里有一个澄清。
Let us declare an interface first.让我们先声明一个接口。
public interface UIUpdater {
void updateUI(int progressValue);
}
Now implement this interface in the activity or fragment where you want to update the UI.现在在要更新 UI 的活动或片段中实现此接口。
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.
}
}
You want to modify the constructor of initializing your AsyncTask
to have the UIUpdater
class to be passed as parameter.您想修改初始化
AsyncTask
的构造函数,以将UIUpdater
类作为参数传递。
public class YourAsyncTask extends AsyncTask<String, Void, String> {
UIUpdater listener;
public YourAsyncTask(UIUpdater listener) {
this.listener = listener;
}
}
So that you can call the AsyncTask
from the activity/fragment using something as following.这样您就可以使用以下内容从活动/片段中调用
AsyncTask
。
YourAsyncTask myTask = new YourAsyncTask(this); // When you are passing this from activity, you are implicitly passing the interface that it implemented.
myTask.execute();
Now from the async task, while you are publishing the progress, invoke the listener function in order to update the UI using the UI thread of your activity/fragment.现在从异步任务中,当您发布进度时,调用侦听器函数以使用您的活动/片段的 UI 线程更新 UI。
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
try {
listener.updateUI(values[0]);
}
}
Hope you get the idea.希望你明白。
Your problem is not that you are using a RecyclerView, your problem is that you're not using it correctly.您的问题不是您使用的是 RecyclerView,而是您没有正确使用它。
It's is neither the adapter, nor the view (RecyclerView) nor even the ViewHolder's responsibility to determine anything here.在这里确定任何事情都不是适配器,也不是视图(RecyclerView),甚至不是 ViewHolder 的责任。
ListAdapter<T, K>
with the corresponding DiffUtilCallback
implementation.ListAdapter<T, K>
和相应的DiffUtilCallback
实现。 If any of these is not true, perform the necessary adjustments so your code can be properly tested and the concerns of each piece of code is better separated.如果其中任何一个不正确,请执行必要的调整,以便正确测试您的代码并更好地分离每段代码的关注点。
Think about it this way, imagine you had all that, and there was a small bug in the "progress value" you update.这样想一想,假设您拥有所有这些,并且您更新的“进度值”中有一个小错误。 Which would be easier, search in the little function in your ViewModel or UseCase/interactor that transforms the Retrofit value into your
<Thing>
, or all over the place in your spaghetti code of callbacks?在您的 ViewModel 或 UseCase/interactor 中搜索将 Retrofit 值转换为您的
<Thing>
的小函数,或者在您的意大利面条式回调代码中的所有地方,哪个会更容易?
Thank you all for trying to help me!!感谢大家试图帮助我!!
This answer helped me to update Circular ProgressBar https://stackoverflow.com/a/42119419/11630822这个答案帮助我更新了 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;
}
In Progressbody class,在 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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.