How to achieve showing a progress bar on each Retrofit 2.0 API call without having to make a progress bar in each activity, showing it and dismissing. Progress bar should show whenever an API is hit and it should dismiss when we get a response on onResponse or onFailure is called.
I tried this :
ProgressDialog mProgressDialog = new ProgressDialog(this);
mProgressDialog.setIndeterminate(true);
mProgressDialog.setMessage("Loading...");
mProgressDialog.show();
retrofitService.login(new SignInRequest(email, password),
new Callback<SignInResponse>() {
#Override
public void onResponse(Call<SignInResponse> call, Response<SignInResponse> response) {
if (mProgressDialog.isShowing())
mProgressDialog.dismiss();
}
#Override
public void onFailure(Call<SignInResponse> call, Throwable t) {
if (mProgressDialog.isShowing())
mProgressDialog.dismiss();
}
});
But this code will have to copy pasted everywhere whenever I make an API call. I don;t want duplicate code.
A base class for your callbacks can be handy.
class BaseCallBack implements CallBacks<T>{
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (mProgressDialog.isShowing())
mProgressDialog.dismiss();
}
#Override
public void onFailure(Call<SignInResponse> call, Throwable t) {
if (mProgressDialog.isShowing())
mProgressDialog.dismiss();
}
}
When you send a request:
retrofitService.login(new SignInRequest(email, password), new MyCallback<SignInResponse>() {
#Override
public void onResponse(Call<SignInResponse> call, Response<SignInResponse> response) {
super.onResponse(call, response);
//do more on response
}
#Override
public void onFailure(Call<SignInResponse> call, Throwable t) {
super.onFailure(call, t);
/* Do more on failure. For example: give a reason why the
request failed*/
}
});
Edit 2019 - using the RXjava adapter:
getUsers()
.subscribeOn(AndroidSchedulers.io()) //run request in the background and deliver response to the main thread aka UI thread
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(() -> showLoading())
.doOnTerminate(() -> hideLoading())
.subscribe(data -> {
//request is successful
}, error -> {
//show error
});
As suggested by #Sourabh, I ended up using a Base activity and calling simple method during each API call. In BaseActivity,
public void showDialog() {
if(mProgressDialog != null && !mProgressDialog.isShowing())
mProgressDialog.show();
}
public void hideDialog() {
if(mProgressDialog != null && mProgressDialog.isShowing())
mProgressDialog.dismiss();
}
In your child activity, you can directly call showDialog() and hideDialog() to show and dismiss dialog.
Based on the answers of Shubham and peter, I wrote a class like this:
class CustomCallBack<T> implements Callback<T> {
private ProgressDialog mProgressDialog;
Context context;
CustomCallBack(Context context) {
this.context = context;
mProgressDialog = new ProgressDialog(context);
((Activity) context).getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
mProgressDialog.setIndeterminate(true);
mProgressDialog.setMessage("Loading...");
mProgressDialog.setCanceledOnTouchOutside(false);
mProgressDialog.show();
}
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
((Activity) context).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
((Activity) context).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
}
}
I hope it will help you.
Shameless Promotion
I've created RxLoading library for that, it can do this and much more,
you can just do something like this:
networkCall().compose(RxLoading.<>create(loadingLayout)).subscribe(...);
it consists out of 2 classes, a custom view (loadingLayout) and RxLoading which is a transformer that glue it all together, you can choose to work with both or either of them.
If you want one progress bar to rule them all, you have several options to achieve it depending on the structure of your app:
one Activity multiple fragments -> just put a loadingLayout in the main screen and tell it to hide the fragment layout when loading
multiple activities: create a base activity which all uses and inject a loadingLayout to all of you views
you can also make a special dialog or activity for the progress bar and use an interface for RxLoading to show and hide it.
RxLoading also supports empty and error states (with built-in retry mechanism, so make sure to check this options as well)
you can find out more on the GitHub page.
Example from tutorial:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
requestWindowFeature(Window.FEATURE_PROGRESS);
ArrayAdapter<Question> arrayAdapter =
new ArrayAdapter<Question>(this,
android.R.layout.simple_list_item_1,
android.R.id.text1,
new ArrayList<Question>());
setListAdapter(arrayAdapter);
setProgressBarIndeterminateVisibility(true);
setProgressBarVisibility(true);
}
Tutorial link:http://www.vogella.com/tutorials/Retrofit/article.html
Related
To summarize my problem:
I have a list of items and a button that I click to query an API
When I click the button, two methods are called. The first method displays a progress bar, clears the list, and uses notifyDataSetChanged()
public void methodOne(){
mProgressBar.setVisibility(View.VISIBLE);
mList.clear;
mAdapter.notifyDataSetChanged();
}
The second method uses retrofit to make a query, and in the callback method, I hide the progress bar, add to the list and call notifyDataSetChanged();
public void methodTwo(){
RetrofitInterfaces.SearchForPosts service = RetrofitClientInstance.getRetrofitInstance()
.create(RetrofitInterfaces.SearchForPosts.class);
Call<Feed> call = service.listRepos(url);
call.enqueue(new Callback<Feed>() {
#Override
public void onResponse(#NonNull Call<Feed> call, #NonNull Response<Feed> response) {
try{
mProgressBar.setVisibility(View.INVISIBLE);
mList.addAll(response.body().getData());
mAdapter.notifyDataSetChanged();
} catch(Exception e){
Log.e(TAG, "Error: " + e);
}
}
#Override
public void onFailure(#NonNull Call<Feed> call, #NonNull Throwable t) {
Log.e(TAG, "onFailure: " + t);
}
});
}
}
My problem is when I call these two ones after another:
methodOne();
methodTwo();
The second method with the retrofit call sometimes returns an IndexOutOfBounds exception because methodOne() calls mList.clear() and mAdapter.notifyDataSetChanged(); while I am making edits to mList.
My question is how can I make the two happen atomically so that they don't interfere with each other?
(I want methodOne() to do everything even before the query happens in methodTwo)
You can use AsyncTask that will execute methodTwo() when methodOne() finished executing
private class MethodsTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
methodOne();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
methodTwo();
}
}
So instead of calling the two methods
methodOne();
methodTwo();
Use this
MethodsTask task = new MethodsTask();
task.execute();
I am making an android application with a lot a activities and Retrofit 2 requests and I want to know what are the best practices for the loading animations during a request ?
Actually, I use my loading animation like this :
public void getLogin() {
showDialog();
retrofitService.getLogin(new Callback<SignInResponse>() {
#Override
public void onResponse(Call<SignInResponse> call, Response<SignInResponse> response) {
hideDialog()
//My stuff
}
#Override
public void onFailure(Call<SignInResponse> call, Throwable t) {
hideDialog()
//My stuff again
}
});
}
public void showDialog() {
ProgressDialog mProgressDialog = new ProgressDialog(this); //Which is deprecated
mProgressDialog.setIndeterminate(true);
mProgressDialog.setMessage("Loading...");
if(mProgressDialog != null && !mProgressDialog.isShowing())
mProgressDialog.show();
}
public void hideDialog() {
if(mProgressDialog != null && mProgressDialog.isShowing())
mProgressDialog.dismiss();
}
I know that ProcessDialog is deprecated since API26 so if you have better ideas, I'll take them !
But it seems like I need to copy past every time this code when I do a requests and I wonder there is a better way to do that.
Thanks for your help :)
onChanged I am updating the UI and cancelling the progressDialog but each time I am rotating the screen the progress dialog shows up and ProgressDialog doesn't dismiss.
How to handle the progressdialog if the data is not changed?
final ProgressDialog pd;
pd=new ProgressDialog(mContext);
pd.setMessage(mContext.getString(R.string.updating));
pd.show();MutableLiveData<BillGetResponse> DisplayByIdLiveData=viewModelForBillDisplay.getBillGetResponseDisplayById(txnRequest);
if (display_by_id) {
DisplayByIdLiveData.observe(BillDisplayFragment.this, new Observer<BillGetResponse>() {
#Override
public void onChanged(#Nullable BillGetResponse billGetResponse) {
if (btnRecordDisableEnabled != null)
btnRecordDisableEnabled.enable();
pd.dismiss(); }
but everytime the onChanged is not trigerred and the progressdialog doesnot stop !!
You need other observer to handle the value of the boolean that indicates if you have to show the ProgressDialog or not. This is the code:
viewModel.getIsLoading().observe(this, new Observer<Boolean>() {
#Override
public void onChanged(#Nullable Boolean aBoolean) {
if(aBoolean){
pd.show();
}else{
pd.dismiss();
}
}
});
In your viewModel add getIsLoading method:
public LiveData<Boolean> getIsLoading(){
LiveData<Boolean> isLoading=yourRepository.getIsLoading();
return isLoading;
}
And in your repository add:
private final MutableLiveData<Boolean> isLoading=new MutableLiveData<>();
and set value to isLoading when start and finish the data upload.
I hope it helps you.
onChanged() not getting called is perfectly normal, it means there are no more changes in data.
I guess you want to show ProgressDialog while data is being fetched or loaded. You can do something like this
In onCreate() Check data is present or not before displaying, below in Pseudo code for displaying ProgressDialog
if (!hasData())
displayProgressDialog()
EDIT:
It seems you want to inform user that data has changed by means of showing ProgressDialog, You can achieve this by showing this for small moment and dismiss it after a while, Something like this in onChanged()
#Override
public void onChanged(#Nullable BillGetResponse billGetResponse) {
if (btnRecordDisableEnabled != null)
btnRecordDisableEnabled.enable();
displayProgressDialog(); //Show progress dialog here
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
pd.dismiss();
}
},500);
}
I'm having the following problem while trying to restore a ProgressDialog from a configuration change. This is my code:
server is a class that does some networking in a thread and after it finishes, it calls the callback on the handler that made the initial call.
public class MainActivity extends FragmentActivity {
private boolean hasAuthDialog = false;
ProgressDialog progressDialog;
// login fragment
public void doLogin(View b) {
boolean ok = true;
if(ok) {
progressDialog = new ProgressDialog(this);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Autenticando");
progressDialog.setCancelable(false);
progressDialog.show();
hasAuthDialog = true;
try {
server.doLogin(cedula.getText().toString(), pass.getText().toString(), new ServerBridgeResponse_CallBack() {
#Override
public void run(Boolean success, Object... args) {
// login finalizado
if(success) {
Toast.makeText(me, "Login success", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(me, "Login error", Toast.LENGTH_LONG).show();
}
progressDialog.dismiss();
hasAuthDialog = false;
}
});
} catch (ServerBridgeNotReadyException e) {
}
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("hasAuthDialog", hasAuthDialog);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
hasAuthDialog = savedInstanceState.getBoolean("hasAuthDialog");
if(hasAuthDialog) {
progressDialog = new ProgressDialog(this);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Autenticando restored");
progressDialog.setCancelable(false);
progressDialog.show();
hasAuthDialog = true;
}
}
The thing is that after an orientation change, when debugging, progressDialog in the doLogin method still refers to the old dialog and not the new one that I created in onRestoreInstance (I know this because of the message. It still says 'Autenticando' instead of 'Autenticando Restored'). What am I doing wrong?
Thanks in advance!
Have you tried making progressDialog static? That way the set variable will be dismissed by the run method, and not the reference to the old progressDialog.
I get it now. The thing is that when there's an orientation change, Android creates an entirely new instance of the activity. Because of this behavior, the keyword this in the callback refers to the previous instance.
One way we could fix this problem is by making the progressDialog static as suggested by Bassiuz, but one different (and IMO more flexible) solution would be to create an static MainActivity me variable and assigning it this at the end of onCreate. After that, in doLogin callback, use me.progressDialog and it should have the reference to the new dialog.
Thanks to Bassiuz!
I am fetching an XML data from the web using HTTP GET/POST.
Right now i have done it in simple way (i.e. without threaed).
According to the below link, i tried to implement it with Progress bar dialog with Thread
http://www.ceveni.com/2009/09/sample-progress-bar-dialog-in-android.html
But how do i come to know about the finish time of fetching XML from the web.(i.e. what should be the sleep time and also when to dismiss the progress bar dialog box)
Let me clear more about my problem => In activity,when the user click on "Fetch data" button,
the "Progress bar" dialog box should be appeared and it should be disappear only when the fetching is completed
successfully.
I think this can be done with "AsyncTask" but dont know how to use that concept for this problem.
So how do i do it ?
public void onClick() {
new FetchTask(context).execute(null);
}
public class FetchTask extends AsyncTask<Object, Object, Object > {
private ProgressDialog dlg;
private Context ctx;
public FetchTask(Context context) {
ctx = context;
}
#Override
protected void onPreExecute() {
dlg = new ProgressDialog(ctx);
dlg.setMessage("Loading....");
super.onPreExecute();
}
#Override
protected void onPostExecute(Object result) {
dlg.dismiss();
if ( result instanceof Exception ) {
// show error message
} else {
// display data
}
super.onPostExecute(result);
}
#Override
protected Object doInBackground(Object... params) {
try {
// String rawData = doPost("yourUrl");
// XMLTree data = yourParser.parse(rawData);
// return data;
} catch ( Exception e ) {
return e;
}
}
}