I'm replacing a sync method for an async one doing the same job. The old method was throwing a custom exception if something went wrong. Now I've got a listener in the new method and I don't know how to keep throwing that exception:
#Override
protected void methodBeingChanged() throws customException {
asyncMethod(new Handler() {
#Override
public void onSuccess(String response, int responseCode) {
}
#Override
public void onError(IOException e) {
}
});
}
This exception is later being catch by another method in another class which gives feedback to the user depending on the exception message.
It is impossible. Your async task is run on another thread and your current methodBeingChanged is finish right away, which means the exception is never thrown by methodBeingChanged.
The only solution is modify the caller code, add another listener method for the specific exception
This may sound silly, but just throw it :p
https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html
#Override
protected void methodBeingChanged() throws customException {
asyncMethod(new Handler() {
#Override
public void onSuccess(String response, int responseCode) {
}
#Override
public void onError() {
try {
throw new customException ();
}catch(customException cEx) {
// Do stuff
}
}
});
}
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 have a method goToNextScreen() which does a check for 3 different asynchronous process, so when all process are done the validation will changes activity (Is kind of a Splash activity)
My example code access from 3 different result callbacks to the activity's method goToNextScreen() updating the flag value for each process and to validate other flags inside.
So far this approach works but i have the next questions:
Is this approach valid? Does it have a risk for some kind of deadlock? all threads/callbacks won't collide accessing the method at the same time causing wrong validations?
class LoadingActivity extends Activity{
public boolean isFetchDone, isAnimationDone, isServiceDone, watchDog;
Presenter presenter;
protected void onCreate(#Nullable Bundle savedInstanceState) {
presenter(this);
runAnimation();
presenter.runGetXService();
runFetchFromDB();
}
//do some generic isAnimationDone
private void runAnimation(){
//animator set and animation assumed to be correct...
//...
animatorSet.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
// do anything before animation start
}
#Override
public void onAnimationEnd(Animator animation) {
isAnimationDone = true;
goToNextScreen();
}
#Override
public void onAnimationCancel(Animator animation) {
// do something when animation is cancelled (by user/ developer)
}
#Override
public void onAnimationRepeat(Animator animation) {
// do something when animation is repeating
}
});
}
//Some example function to fetch data from x DB
private void runFetchFromDB() {
final Realm realm = RealmProvider.getInstance();
final ThingDB db = new ThingDBImpl(realm);
realm.beginTransaction();
db.getData(10L)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<XData>() {
#Override
public void onCompleted() {
isFetchDone = true;
goToNextScreen();
}
#Override
public void onError(Throwable e) {
//we dont care about the result
isFetchDone = true;
goToNextScreen();
}
#Override
public void onNext(XData dataSaved) {
//Should i update isFetchDone here?
});
realm.cancelTransaction();
}
private synchronized void goToNextScreen(){
if(!watchDog) return;
if(isFetchDone && isAnimationDone && isServiceDone){
changeActivityFaded(this, SomeOtherActivity);
finish();
watchDog = true;
}
}
}
class Presenter {
Activity activity;
Presenter(Activity activity){
this.activity = activity;
}
public void runGetXService(){
new Presenter.GetServiceAsyncTask().execute();
}
private class GetServiceAsyncTask extends AsyncTask<Void, Void, Response> {
#Override
protected void onPreExecute() {
super.onPreExecute();
//do preparation for service response, etc, asumme all correct
}
#Override
protected XResponse doInBackground(Void... voids) {
try {
return //Assume correct behaviour...
} catch (NetworkConnectionException e) {
return null;
}
}
#Override
protected void onPostExecute(XResponse xResponse) {
super.onPostExecute(xResponse);
((LoadingActivity)activity).isServiceDone = true;
((LoadingActivity)activity).goToNextScreen();
}
}
}
EDIT:
I have changed the method goToNextScreen to synchronized so it supposed to not allow access from others threads at the same time. Still have doubts if withs the execution will be right.
Yes, making the method synchronized means it cannot be executed multiple times simultaneously. If a second thread calls it while it is still executing, the second thread will block until the synchronization lock is released.
https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
So far this approach by adding the synchronized to the method goToNextScreen member of the activity instance shared on the threads accessing it has worked so far. (View the question code for solution). Although i need to add a watch dog just in case some thread pass to execute code that its supposed to execute just once.
My fragment is
public class sample extends Fragment implements statusChanger{
void onResume() {
Listener.registerListener(this);
}
void onPause() {
Listener.deRegisterListener(this);
}
#Override
public void onStatusChanged() {
MyTextView.setVisibility(View.VISIBLE);
}
}
My Interface is
public interface StatusChanger {
void onStatusChanged();
}
My callback from another java class is
public void onstatusChanged() {
listener.onStatusChanged();
}
The above is the outline of my code , I could get a call back from the ordinal java class to my fragment , but the textView is not set to visible and i do not get any runtime errors.
Looks like it might be a thread issue. What if you run the onStatusChanged() code on the UI thread?
#Override
public void onStatusChanged() {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
MyTextView.setVisibility(View.VISIBLE);
}
});
}
In normal cases, following code will work:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// Update UI here.
}
});
Note: If you have wrap above code with Handler with a delay then it may not work.
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.
I am trying to get the Google Play Services working as following using AsyncTaks:
1 - The Login Dialog is only displayed when the user click on the leaderboard button which call the method loginGPRS() below;
2 - An ansync task is then executed which call the startLoginGRPS() method on the onPreExecute
3 - [Here is the problem] Once he is logged in, I want to call the methods submitScoreGPRS() and getLeaderBoardGPRS() in onPostExecute method, but the leaderboard dialog is never opened...
Here is the relevant source code:
#Override
public void loginGPGS() {
try {
MyAsyncTask asyncTask_a = new MyAsyncTask();
asyncTask_a.execute();
} catch (final Exception ex) {
}
}
class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {
String errorMsg;
#Override
protected void onPreExecute() {
startLoginGPGS();
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Void... v) {
return true;
}
#Override
protected void onPostExecute(Boolean success) {
if(success){
submitScoreGPGS(bestScore);
getLeaderboardGPGS();
}
}
}
public void startLoginGPGS() {
try {
// runOnUiThread(new Runnable() {
// public void run() {
gameHelper.beginUserInitiatedSignIn();
// }
// });
} catch (final Exception ex) {
}
}
#Override
public void submitScoreGPGS(int score) {
if(getSignedInGPGS()){
Games.Leaderboards.submitScore(gameHelper.getApiClient(),
getString(R.string.leaderboard1_id), score);
}
}
Have you trace your code??
Write something like...
Log.d("Trace","Point X"):
In the doInBackground method just before the return true, another one in the onPostExecute method just before the if and inside the if.
Take a look at your Logcat and come back...
Hope it helps.
You're executing your method on the UI thread, because onPostExecute() runs on the UI thread and your method has network execution..
You should move your method to the doInBackground() method