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();
Related
There is a String message as a parameter in an interface method:
public class Home extends AppCompatActivity {
private String globalStringResult = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_activity);
getMediaInfo();
Log.d("Result: ", globalStringResult); // Here result is Null
}//TODO OnCreate End
private void getMediaInfo() {
FFmpeg.getInstance(this).execute(new String[]{"-version"},
new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
globalStringResult = message;
}
});
}
}
Here is this problem that I've faced many times but always ran from it. Now I want to deal with it if you help me.
I am executing getMediaInfo() method inside onCreate. When I log the result inside onCreate after getMediaInfo() execution , the result would be null. But if I run it inside an onClick button or something I get my desired result.
Is there any way that I could return callback message anywhere that I want?
Sounds like your function getMediaInfo is asynchronous which means it could take some time before the onSuccess block is called and your value is set. Instead of relying on a variable I would suggest to use a callback function. That way your message will be passed to the callback function and you could use it anywhere.
public interface MyCallback{
void success(String message);
}
Then you would need to modify your function as follows. Then where ever the callback is implemented you will receive the message and you can act on the value.
public void getMediaInfo(MyCallback callback){
FFmpeg.getInstance(this).execute(cmdArray, new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
callback.success(message);
}
});
If your further actions depend on the value set in onSuccess callback then simply call a function from this callback method. You need to provide more info on what exactly you are trying to do with this variable.
Asynchronous calls can be tricky but you have to wait until it is finished before the variable is available. This means calling any methods that rely on the variable in the callback of the async call. There's really no way around it. You may want to make two version of the call; one for onCreate and one when you need to call it from other places.
public class Home extends AppCompatActivity {
private String globalStringResult = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_activity);
getMediaInfoOnCreate();
// move any code below this line into the new method
}//TODO OnCreate End
private void getMediaInfo() {
FFmpeg.getInstance(this).execute(new String[]{"-version"},
new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
globalStringResult = message;
codeThatNeedsToBeRunDuringOnCreateButAfterSettingGlobalStringResult();
}
});
}
private void getMediaInfoOnCreate() {
FFmpeg.getInstance(this).execute(new String[]{"-version"},
new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
globalStringResult = message;
}
});
}
private void codeThatNeedsToBeRunDuringOnCreateButAfterSettingGlobalStringResult() {
// put your moved code from onCreate here
Log.d("Result: ", globalStringResult); // Here correct result will be logged
}
}
I want to replace my AsyncTask with RxJava in android. My current AsyncTask goes like this:
public class ProgressBarAsyncTask extends AsyncTask<Void,Void,Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
ringProgressDialog = ProgressDialog.show(context,"MyProgressBarTitle","Working please wait",true, false);
}
#Override
protected Void doInBackground(Void... void) {
//do work
myTask();
return null;
}
#Override
protected void onPostExecute(Void void) {
super.onPostExecute();
ringProgressDialog.dismiss();
}
}
Here's my RxJava replacement:
public static Observable<Void> getObservable(final Context context,final String... params) {
return Observable.defer(new Func0<Observable<Void>>() {
#Override
public Observable<Void> call() {
return Observable.just(myTask());
}
});
}
public static Subscriber<Void> getSubscriber() {
Subscriber<Void> subscriber = new Subscriber<Void>() {
#Override
public void onCompleted() {
ringProgressDialog.dismiss();
}
#Override
public void onError(Throwable e) {
Log.d(TAG,e.toString());
}
#Override
public void onNext(Void aVoid) {
manipulateData();
}
};
return subscriber;
}
My Activity:
public class MainActivity extends Activity {
private ProgressDialog ringProgressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GetNumberObservable.Observable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()))
.subscribe(getSubscriber());
}
}
How do I mimic the onPreExecute() method in the AsyncTask where I kick off the progressDialog?
Here is how I would do it:
public final class ProgressOrResult<T> {
final int progress;
final T result;
public ProgressOrResult(int progress, T result) {
this.progress = progress;
this.result = result;
}
}
ProgressDialog ringProgressDialog = ProgressDialog.show(
context, "MyProgressBarTitle", "Working please wait", true, false);
Observable.fromEmitter((Emitter<ProgressOrResult> emitter) -> {
// generate "progress"
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
emitter.onNext(new ProgressOrResult(i, null));
Thread.sleep(1);
}
// generate "result"
emitter.onNext(new ProgressOrResult(100, sum));
emitter.onComplete();
}, BackpressureMode.BUFFER)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (pr.result == null) {
ringProgressDialog.setProgress(pr.progress);
} else {
ringProgressDialog.dismiss();
// process the result here
}
}, error -> {
// handle error here
})
In RxJava you have the do operators, that creates Observable
lifecycle events listeners, in your case you want to do something
(update the UI) before the task started, which mean you want the
doOnSubscribe event. (side note it is true with 'cold' Observables
that started thier work when subscrbied to - like your case) Just
beware to call .observeOn(AndroidSchedulers.mainThread())) before
the doOnSubscribe in order to get notified on the mainThread, as
you're updating the UI.
Instead of using both defer and just
return Observable.defer(new Func0<Observable<Void>>() {
#Override
public Observable<Void> call() {
return Observable.just(myTask());
}
});
you can use fromCallable:
Observable.fromCallable(new Callable<Object>() {
#Override
public Object call() throws Exception {
return myTask();
}
})
I am showing progressBar indoOnSubscribe() , hiding in doOnError() and in subscribe().
Please refer to this link for more details.
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 RxLoading with a simple progress bar you just need to implement an interface and you are done.
you can check more on the GitHub page.
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
I have to send some things from the database to the server. So, I call a db method, and get a cursor, which has many positions. I need to send a httprequest for avery position of the cursor, but only send the second petition when the first has been received in the server, and the proper answer sent. I have this code:
final Cursor cursor = db.getData();
Thread thread=new Thread(new Runnable() {
#Override
public void run() {
if(cursorContadores.moveToFirst()){
do{
Call<String> peticion = interfaz.saveToServer(cursor.getString(1), cursor.getString(2));
peticion.enqueue(new Callback<String>() {
#Override
public void onResponse(Response<String> response, Retrofit retrofit) {
if(response.message().equals("ok")){
}else{
}
}
#Override
public void onFailure(Throwable t) {
}
});
}while (cursorContadores.moveToNext());
cursorContadores.close();
db.close();
}
}
});
thread.start();
This way, I think it will not wait until every iteration in the do while block ends, to start the next iteration.
How could I achieve this? Something I could read to learn how to?
Thank you.
In your Http response, you can check for response code.
For example if the response is 200 for successful receive, you can do something like:
HTTPResponse response;
if(response.getStatusLine().getStatusCode()==200){
//SEND NEXT
}
This way you can send the next request once the previous one is received.
I hope I understood the question correctly.
Thank you.
This can most definitely be done AsyncTask... Handle the network request in doInBackground() and once doInBackground() is finished, onPostExecute() is triggered on the response and that's where you can execute any code that send the second petition.
If you need something a bit more generic and re-usable, you would probably want to implement a callback.. I'll refer to the next step as the client and the AsyncTask as the server.
Create a new interface and create some method stubs.
public interface MyEventListener {
public void onEventCompleted();
public void onEventFailed();
}
Have your client pass instance of MyEventListener to the server. A typical way of doing this is to have your client implement the interface (MyEventListener) and pass itself to the server.
public class MyActivity implement MyEventListener {
public void startEvent() {
new MyAsyncTask(this).execute();
}
#Override
public void onEventCompleted() {
// TODO
}
#Override
public void onEventFailed() {
// TODO
}
}
On the onPostExecute of the server, check if the callback is null and call the appropriate method.
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private MyEventListener callback;
public MyAsyncTask(MyEventListener cb) {
callback = cb;
}
[...]
#Override
protected void onPostExecute(Void aVoid) {
if(callback != null) {
callback.onEventCompleted();
}
}
}
Check similar reference
learn more reference
I'm using a swiperefreshlayout android.support.v4.widget.SwipeRefreshLayout, and using rxjava to query to server every time a swipe happens. My code is like below, I have to create duplicate observable and subscriber inside the OnRefreshListener for them to get called, which looks quite bad due to code duplication. If I use the original ones (declared outside), then the subscriber is never returned (no onNext, onError, onCompleted triggered). What am I missing with RxJava in this case?
Subscriber<ListVerifyResponseWrapper> subscriber = new Subscriber<ListVerifyResponseWrapper>() {
#Override
public void onCompleted() {
LogUtils.LOGD(TAG, "completed");
}
#Override
public void onError(Throwable e) {
Toast.makeText(getActivity(), getString(R.string.An_error_has_occured), Toast.LENGTH_SHORT).show();
mSwipeRefreshLayout.setRefreshing(false);
}
#Override
public void onNext(ListVerifyResponseWrapper listVerifyResponseWrapper) {
changeViewStateAccordingToResult(listVerifyResponseWrapper);
}
};
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
Observable<ListVerifyResponseWrapper> observableDuplicate = RestClient.getInstance().getRestApi().getListVerifyRequestDetail(model);
final Subscriber<ListVerifyResponseWrapper> subscriberDuplicate = new Subscriber<ListVerifyResponseWrapper>() {
#Override
public void onCompleted() {
LogUtils.LOGD(TAG, "completed");
}
#Override
public void onError(Throwable e) {
Toast.makeText(getActivity(), getString(R.string.An_error_has_occured), Toast.LENGTH_SHORT).show();
mSwipeRefreshLayout.setRefreshing(false);
}
#Override
public void onNext(ListVerifyResponseWrapper listVerifyResponseWrapper) {
changeViewStateAccordingToResult(listVerifyResponseWrapper);
}
};
observableDuplicate.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread()).subscribe(subscriberDuplicate);
}
});
observable.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread()).subscribe(subscriber);
Thanks
I had the exact same problem. I solved it by chaining
.observeOn(AndroidSchedulers.mainThread())
right before the final subscriber. Apparently you have to make sure that stuff concerning the UI is done on the UI thread
On top of Johnnycube's answer (which is correct), I suggest you to use
subscribeOn(Schedulers.io())
which is meant for processing callbacks, amongst other things.