Android Draw Polygone on Google Map after HTTP request - android

Hello I am trying to draw polygones on a google map after I get the data for them from an HTTP call. I always get the same error:
FATAL EXCEPTION: OkHttp Dispatcher
Process: com.example.rtuya.secmerev2, PID: 1011
java.lang.IllegalStateException: Not on the main thread
Here is how I do my HTTP call and then how I try to draw my areas:
private void getZones() throws JSONException {
Request request = new Request.Builder().url(getString(R.string.main_url) + "/api/getZones")
.headers(buildStandardHeaders(Stormpath.accessToken()))
.get()
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
#Override public
void onFailure(Call call, IOException e) {
Log.d("DEBUG", "ERROR");
}
#Override public void onResponse(Call call, Response response)throws IOException {
try {
JSONArray responseArray = new JSONArray(response.body().string());
zones = new ArrayList<ZoneData>();
for (int i=0; i < responseArray.length(); i++) {
db.addZone(new ZoneData(responseArray.getJSONObject(i)));
zones.add(new ZoneData(responseArray.getJSONObject(i)));
}
isTrackingServiceRunning = startService(new Intent(ActivityMain.this, ServiceTracking.class));
bindService(new Intent(ActivityMain.this, ServiceTracking.class), trackerServiceConnection, Context.BIND_AUTO_CREATE);
drawAreasOnDashboard();
} catch (JSONException e) {
e.printStackTrace();
};
}
});
}
Below is how I try to draw my Areas and the error always happens on the line that contains areaMap.drawPolygones():
public void drawAreas() {
int polygoneFillingIndex = 1;
if(ActivityMain.zones != null) {
for (ZoneData zone : ActivityMain.zones) {
int color;
if ((polygoneFillingIndex % 2) == 0) {
color = R.color.polygoneFillingBlue;
} else {
color = R.color.polygoneFilingGreen;
}
areasMap.addPolygon(new PolygonOptions()
.add(zone.getP1(), zone.getP2(), zone.getP3(), zone.getP4())
.strokeColor(ResourcesCompat.getColor(getResources(), R.color.polygoneStroke, null))
.fillColor(ResourcesCompat.getColor(getResources(), color, null))
.zIndex(Float.valueOf(zone.getPosititionInArray()))
.clickable(true));
polygoneFillingIndex++;
}
}
}

That error logs says exactly where you are wrong. Android does not allow you to run http requests on the main thread so that it won't be blocked by it.
You have to encapsulate those http requests in AsyncTasks, you can find tons of examples here on SO or anywhere in the web

Related

Retrofit - how do I make a synchronous request within an asynchronous request

I'm implementing a two-level nested recyclerView and both recycler views make an API call using retrofit. This is the method that makes the synchronous request:
public void loadSectionStories(String sessionKey, CuratedSection section) {
Call<JsonArray> subCall;
subCall = TravelersApi.endpoint().getCuratedSectionTopics(sessionKey, section.id);
try {
Response<JsonArray> response = subCall.execute();
if(response.code() != 200) {
Toast.makeText(getApplicationContext(), "Cannot load page as of the moment.", Toast.LENGTH_SHORT).show();
return;
}
JsonArray rawStories = response.body();
if(rawStories.size() == 0) {
//TODO: show placeholder
return;
}
ArrayList<CuratedSectionItem> stories = new ArrayList<>();
for(int i = 0; i < rawStories.size(); i++) {
JsonObject jStories = rawStories.get(i).getAsJsonObject();
JSONObject temp = new JSONObject(jStories.toString());
JsonObject author = jStories.get("author").getAsJsonObject();
CuratedSectionItem story = new CuratedSectionItem();
story.title = jStories.get("title").getAsString();
story.avatar = author.get("profile_photo").getAsString();
story.displayPhoto = temp.getString("primary_photo");
story.username = author.get("username").getAsString();
story.description = jStories.get("content").getAsString();
story.topicId = jStories.get("id").getAsString();
story.postId = jStories.get("first_post_id").getAsString();
story.hasReacted = false;
story.upvotes = jStories.get("stats").getAsJsonObject().get("upvotes").getAsInt();
stories.add(story);
}
section.stories = stories;
} catch (IOException e) {
Log.d("ERROR!", e.toString());
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
This is the method that makes the asynchronous request and also calls loadSectionStories in a thread:
public void loadCuratedSections(final int start, final int limit) {
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
final String sessionKey = prefs.getString("session_key", null);
Call<JsonArray> call;
call = TravelersApi.endpoint().getCuratedSections(sessionKey);
call.enqueue(new Callback<JsonArray>() {
#Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
if(response.code() != 200) {
Toast.makeText(getApplicationContext(), "Cannot load page as of the moment.", Toast.LENGTH_SHORT).show();
return;
}
JsonArray rawSections = response.body();
if(rawSections.size() == 0) {
return;
}
for (int i = start; i < limit; i++) {
JsonObject jSection = rawSections.get(i).getAsJsonObject();
final CuratedSection section = new CuratedSection();
section.id = jSection.get("id").getAsString();
section.header = jSection.get("section_header").getAsString();
section.isShown = jSection.get("is_shown").getAsBoolean();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
loadSectionStories(sessionKey, section);
}
});
thread.start();
curatedSections.add(section);
}
}
#Override
public void onFailure(Call<JsonArray> call, Throwable t) {
Log.d("ERROR!", t.toString());
t.printStackTrace();
}
});
}
Everything is working fine except the fact that section.stories returns null. It doesn't make sense to me because of this statement section.stories = stories inside loadSectionStories.
If you are using section.stories before your synchronous request is completed (which is running in new threads) then it will return null which is currently happening.
so either you have to remove new thread flow if you want to use it after your first asynchronous request is completed,
or you have to reload your recycler view when you stories is updated.
Also why are you executing your synchronous request(loadSectionStories) in new thread, is it not similar to asynchronous request?
Retrofit asyncRetrofit = new Retrofit.Builder()
.baseUrl(URLS.MAIN_SERVER_URL)
// below line create thread for syncrouns request
.callbackExecutor(Executors.newSingleThreadExecutor())
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
this will run your request in asyncronous

How to dismiss dialog when calling retrofit multiple times?

I am using retrofit to access API with counter value, i.e. when I
getting first time response calling retrofit method again and again
till the last counter value.I also do after getting response download
image using AsyncTask.
When i first time calling the retrofit method, Showing Progress
Dialog. I want to dismiss dialog when last image downloaded.
visibleProgressBar(getString(R.string.fetch_data_msg));
getMyOxygenInitialSync();
public void getMyOxygenInitialSync() {
if (new CheckConnection(context).isConnectedToInternet()) {
UserPreferences preferences = UserPreferences.getUserPreferences(context);
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
try {
JsonObject params = new JsonObject();
params.addProperty("my_oxygen_user_id", "984");
params.addProperty("action", "fullSync");
params.addProperty("page", String.valueOf(page));
params.addProperty("syncDate", "");
// RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(new JSONObject(params)).toString());
Call<MyOxygenSyncResponseMain> call = apiService.getMyOxygenSycList(params);
call.enqueue(new Callback<MyOxygenSyncResponseMain>() {
#Override
public void onResponse(Call<MyOxygenSyncResponseMain> call, retrofit2.Response<MyOxygenSyncResponseMain> response) {
invisibleProgressDialog();
try {
if (response != null) {
/* MyOxygenSyncResponseMain myOxygenSyncResponseMain = new MyOxygenSyncResponseMain();
myOxygenSyncResponseMain = ((MyOxygenSyncResponseMain)response);*/
totalPage = response.body().getTotalPage();
page++;
if (response.body().getImageDetail() != null && response.body().getImageDetail().size() > 0) {
for (int i = 0; i < response.body().getImageDetail().size(); i++) {
MyOxygen myOxygen = new MyOxygen();
myOxygen.setImageId(response.body().getImageDetail().get(i).getMy_oxygen_image_id());
myOxygen.setImageName(response.body().getImageDetail().get(i).getMy_oxygen_image_name());
myOxygen.setImagePhysicalPath(response.body().getImageDetail().get(i).getMy_oxygen_image_url());
myOxygen.setCategory(response.body().getImageDetail().get(i).getMy_oxygen_image_cat());
myOxygen.setSyncDate(response.body().getImageDetail().get(i).getSyncDate());
// myOxygen.setOrderId(myOxygenSyncResponseMain.getImageDetail().get(i).getOrde());
myOxygen.setIsUpdated(Integer.parseInt(response.body().getImageDetail().get(i).getMy_oxygen_is_deleted()) == 0 ? 1 : 0);
myOxygen.setIsDeleted(response.body().getImageDetail().get(i).getMy_oxygen_is_deleted());
myOxygenDBHelpher.saveMyOxygen(myOxygen);
downloadImage(myOxygen.getImageName(),myOxygen.getImagePhysicalPath(),myOxygen.getImageId());
}
for(page=page;page<=totalPage+1;page++){
getMyOxygenInitialSync();
}
/* if( page== totalPage){
downloadImage();
}*/
}
Log.d("Resp ", response.raw().body().toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<MyOxygenSyncResponseMain> call, Throwable t) {
invisibleProgressDialog();
try {
Log.d("Error ", call.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}

I want to refresh/recreate my activity programatically when getting response from server

I want when server sends some response in form of WebView then immediately my activity gets refreshed and so WebView in form of banner ad.
I write code for display banner ad but ad is showing only when my activity recreated i.e. when I rotate my screen then banner is showing but when it is in same static mode then banner is not showing.
So, please let me know what I will do so that when server gave some response immediately it will be shown on my activity.
void startDemo() {
//Set Http Client Options
final OptimusHTTP client = new OptimusHTTP();
client.enableDebugging();
client.setMethod(OptimusHTTP.METHOD_POST);
client.setMode(OptimusHTTP.MODE_SEQ);
FreqDetector_Goertzel.getInstance().startRecording(new FreqDetector_Goertzel.RecordTaskListener() {
private String urlRedirect = "";
private String imgSmallBanner = "";
#Override
public void onSuccess(int val)
{
String pSet = pVal.getPatternSet(val, 5);
if (pSet != null) {
FreqDetector_Goertzel.getInstance().stopRecording();
EasyDeviceInfo deviceInfo = new EasyDeviceInfo(MainActivity.this);
final HashMap<String, String> device_params = new HashMap<>();
device_params.put("aid", deviceInfo.getAndroidID());
device_params.put("pattern", pSet);
if (isNetworkAvailable(MainActivity.this)) {
try {
client.makeRequest(MainActivity.this, new HttpReq(), Defaults.MATCHINGSERVER, device_params, new OptimusHTTP.ResponseListener() {
#Override
public void onSuccess(String s) {
try {
if (s != null && !s.contains("No Match Found"))
{
JSONObject jsonObject = null;
jsonObject = new JSONObject(s);
imgSmallBanner = Uri.decode(jsonObject.optString("smallImgUrl", "NA"));
urlRedirect = Uri.decode(jsonObject.optString("redirectUrl", "NA"));
loadAdvertisement(urlRedirect, imgSmallBanner);
} else {
//Did not match
startDemo();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(String s) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
} else {
//Internet not available. Do not do anything.
}
}
}
#Override
public void onFailure(String s) {
}
});
}
void loadAdvertisement(String clickUrl, String imgSmallName) {
String click_url;
String img_small_url;
stopDemo();
click_url = Uri.decode(Uri.encode(clickUrl));
img_small_url = imgSmallName;
StringBuilder htmlData2 = new StringBuilder();
htmlData2.append("<html><body style='margin:0;padding:0;background-color:black;'><a href='").append(click_url).append("' ><img src='").append(img_small_url).append("' height=50 style='margin:0 auto;display:block;' /></a></body></html>");
webView_img_small.loadDataWithBaseURL("file:///android_asset/", htmlData2.toString(), "text/html", "utf-8", null);
webView_img_small.setVisibility(View.VISIBLE);
/* What I will do here so when server sends response it will immediately being refreshed and shown on activity without recreating it.*/ }
here you can find some response: http://developer.android.com/guide/topics/ui/how-android-draws.html
for me a call to invalidate() only refresh the view and a call to requestLayout() refresh the view and compute the size of the view in the screen.
You can try to use Activity.recreate(). This method will destroy your current Activity and create a new Activity same way when you rotate device.
Hope this helps.

How to retry HTTP requests with OkHttp/Retrofit?

I am using Retrofit/OkHttp (1.6) in my Android project.
I don't find any request retry mechanism built-in to either of them. On searching more, I read OkHttp seems to have silent-retries. I don't see that happening on any of my connections (HTTP or HTTPS). How to configure retries with okclient ?
For now, I am catching exceptions and retrying maintaining a counter variable.
For Retrofit 2.x;
You can use Call.clone() method to clone request and execute it.
For Retrofit 1.x;
You can use Interceptors. Create a custom interceptor
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = chain.proceed(request);
int tryCount = 0;
while (!response.isSuccessful() && tryCount < 3) {
Log.d("intercept", "Request is not successful - " + tryCount);
tryCount++;
// retry the request
response.close()
response = chain.proceed(request);
}
// otherwise just pass the original response on
return response;
}
});
And use it while creating RestAdapter.
new RestAdapter.Builder()
.setEndpoint(API_URL)
.setRequestInterceptor(requestInterceptor)
.setClient(new OkClient(client))
.build()
.create(Adapter.class);
I don't know if this is an option for you but you could use RxJava together with Retrofit.
Retrofit is able to return Observables upon rest calls. On Oberservables you can just call retry(count) to resubscribe to the Observable when it emits an error.
You would have to define the call in your interface like this:
#GET("/data.json")
Observable<DataResponse> fetchSomeData();
Then you can subscribe to this Observable like this:
restApi.fetchSomeData()
.retry(5) // Retry the call 5 times if it errors
.subscribeOn(Schedulers.io()) // execute the call asynchronously
.observeOn(AndroidSchedulers.mainThread()) // handle the results in the ui thread
.subscribe(onComplete, onError);
// onComplete and onError are of type Action1<DataResponse>, Action1<Throwable>
// Here you can define what to do with the results
I had the same problem like you and this was actually my solution. RxJava is a really nice library to use in combination with Retrofit. You can even do many cool things in addition to retrying (like e.g. composing and chaining calls).
I am of the opinion that you shouldn't mix API handling (done by retrofit/okhttp) with retries. Retrying mechanisms are more orthogonal, and can be used in many other contexts as well. So I use Retrofit/OkHTTP for all the API calls and request/response handling, and introduce another layer above, for retrying the API call.
In my limited Java experience so far, I have found jhlaterman's Failsafe library (github: jhalterman/failsafe) to be a very versatile library for handling many 'retry' situations cleanly. As an example, here's how I would use it with a retrofit instantiated mySimpleService, for authentication -
AuthenticationResponse authResp = Failsafe.with(
new RetryPolicy().retryOn(Arrays.asList(IOException.class, AssertionError.class))
.withBackoff(30, 500, TimeUnit.MILLISECONDS)
.withMaxRetries(3))
.onRetry((error) -> logger.warn("Retrying after error: " + error.getMessage()))
.get(() -> {
AuthenticationResponse r = mySimpleAPIService.authenticate(
new AuthenticationRequest(username,password))
.execute()
.body();
assert r != null;
return r;
});
The code above catches socket exceptions, connection errors, assertion failures, and retries on them maximum of 3 times, with exponential backoff. It also allows you to customise on-retry behaviour, and allows you to specify a fallback as well. It's quite configurable, and can adapt to most of the retry situations.
Feel free to check the documentation of the library as it offers many other goodies apart from just retries.
The problem with response.isSuccessful() is when you have an exception like SocketTimeoutException.
I modified the original code to fix it.
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
boolean responseOK = false;
int tryCount = 0;
while (!responseOK && tryCount < 3) {
try {
response = chain.proceed(request);
responseOK = response.isSuccessful();
}catch (Exception e){
Log.d("intercept", "Request is not successful - " + tryCount);
}finally{
tryCount++;
}
}
// otherwise just pass the original response on
return response;
}
});
Hope it helps.
Regards.
Courtesy to the top answer,This is what worked for me. If there is a connectivity issues, its better to wait for a few seconds before retry.
public class ErrorInterceptor implements Interceptor {
ICacheManager cacheManager;
Response response = null;
int tryCount = 0;
int maxLimit = 3;
int waitThreshold = 5000;
#Inject
public ErrorInterceptor() {
}
#Override
public Response intercept(Chain chain){
// String language = cacheManager.readPreference(PreferenceKeys.LANGUAGE_CODE);
Request request = chain.request();
response = sendReqeust(chain,request);
while (response ==null && tryCount < maxLimit) {
Log.d("intercept", "Request failed - " + tryCount);
tryCount++;
try {
Thread.sleep(waitThreshold); // force wait the network thread for 5 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
response = sendReqeust(chain,request);
}
return response;
}
private Response sendReqeust(Chain chain, Request request){
try {
response = chain.proceed(request);
if(!response.isSuccessful())
return null;
else
return response;
} catch (IOException e) {
return null;
}
}
}
A solution that worked for me on OkHttp 3.9.1 (considering other answers for this question):
#NonNull
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request request = chain.request();
int retriesCount = 0;
Response response = null;
do {
try {
response = chain.proceed(request);
// Retry if no internet connection.
} catch (ConnectException e) {
Log.e(TAG, "intercept: ", e);
retriesCount++;
try {
Thread.sleep(RETRY_TIME);
} catch (InterruptedException e1) {
Log.e(TAG, "intercept: ", e1);
}
}
} while (response == null && retriesCount < MAX_RETRIES);
// If there was no internet connection, then response will be null.
// Need to initialize response anyway to avoid NullPointerException.
if (response == null) {
response = chain.proceed(newRequest);
}
return response;
}
I found the way(OKHttpClient intercepter) provided by Sinan Kozak does not work when http connection failed, there is nothing yet concerned with HTTP response.
So i use another way to hook the Observable object, call .retryWhen on it.
Also, i have added retryCount limit.
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.HttpException;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import rx.Observable;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
Then
RxJavaCallAdapterFactory originCallAdaptorFactory = RxJavaCallAdapterFactory.create();
CallAdapter.Factory newCallAdaptorFactory = new CallAdapter.Factory() {
#Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
CallAdapter<?> ca = originCallAdaptorFactory.get(returnType, annotations, retrofit);
return new CallAdapter<Observable<?>>() {
#Override
public Type responseType() {
return ca.responseType();
}
int restRetryCount = 3;
#Override
public <R> Observable<?> adapt(Call<R> call) {
Observable<?> rx = (Observable<?>) ca.adapt(call);
return rx.retryWhen(errors -> errors.flatMap(error -> {
boolean needRetry = false;
if (restRetryCount >= 1) {
if (error instanceof IOException) {
needRetry = true;
} else if (error instanceof HttpException) {
if (((HttpException) error).code() != 200) {
needRetry = true;
}
}
}
if (needRetry) {
restRetryCount--;
return Observable.just(null);
} else {
return Observable.error(error);
}
}));
}
};
}
};
Then
add or replace
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
with
.addCallAdapterFactory(newCallAdaptorFactory)
For example:
return new Retrofit
.Builder()
.baseUrl(baseUrl)
.client(okClient)
.addCallAdapterFactory(newCallAdaptorFactory)
.addConverterFactory(JacksonConverterFactory.create(objectMapper));
Note: For simplicity, i just treat HTTP code > 404 code as retry, please modify it for yourself.
Besides, if http response is 200, then above rx.retryWhen will not get called, if you insist check such a response, you can add rx.subscribeOn(...throw error... before .retryWhen.
For those prefer an interceptor to deal with the issue of retrying -
Building upon Sinan's answer, here is my proposed interceptor, which includes both retry count and back-off delay, and only retries attempts when network is available, and when request wasn't cancelled.
(only deals with IOExceptions (SocketTimeout, UnknownHost, etc.))
builder.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = null;
int tryCount = 1;
while (tryCount <= MAX_TRY_COUNT) {
try {
response = chain.proceed(request);
break;
} catch (Exception e) {
if (!NetworkUtils.isNetworkAvailable()) {
// if no internet, dont bother retrying request
throw e;
}
if ("Canceled".equalsIgnoreCase(e.getMessage())) {
// Request canceled, do not retry
throw e;
}
if (tryCount >= MAX_TRY_COUNT) {
// max retry count reached, giving up
throw e;
}
try {
// sleep delay * try count (e.g. 1st retry after 3000ms, 2nd after 6000ms, etc.)
Thread.sleep(RETRY_BACKOFF_DELAY * tryCount);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
tryCount++;
}
}
// otherwise just pass the original response on
return response;
}
});
I have play a lot with this problem trying to find how is the best way to retry Retrofit requests. I am using Retrofit 2 so my solution is for Retrofit 2. For Retrofit 1 you have to use Interceptor like the accepted answer here. The answer of #joluet is correct but he did not mention that retry method need to be called before .subscribe(onComplete, onError) method. This is very important otherwise the request wouldn't be retried again like #pocmo mentioned in #joluet answer. Here is my example:
final Observable<List<NewsDatum>> newsDetailsObservable = apiService.getCandidateNewsItem(newsId).map((newsDetailsParseObject) -> {
return newsDetailsParseObject;
});
newsDetailsObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry((integer, throwable) -> {
//MAX_NUMBER_TRY is your maximum try number
if(integer <= MAX_NUMBER_TRY){
return true;//this will retry the observable (request)
}
return false;//this will not retry and it will go inside onError method
})
.subscribe(new Subscriber<List<NewsDatum>>() {
#Override
public void onCompleted() {
// do nothing
}
#Override
public void onError(Throwable e) {
//do something with the error
}
#Override
public void onNext(List<NewsDatum> apiNewsDatum) {
//do something with the parsed data
}
});
apiService is my RetrofitServiceProvider object.
BTW : I am using Java 8 so a lot of lambda expressions are inside the code.
Just want to share my version. It uses rxJava retryWhen method. My version retries connection every N=15 sec and almost immediately emit retry when internet connection recover.
public class RetryWithDelayOrInternet implements Function<Flowable<? extends Throwable>, Flowable<?>> {
public static boolean isInternetUp;
private int retryCount;
#Override
public Flowable<?> apply(final Flowable<? extends Throwable> attempts) {
return Flowable.fromPublisher(s -> {
while (true) {
retryCount++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
attempts.subscribe(s);
break;
}
if (isInternetUp || retryCount == 15) {
retryCount = 0;
s.onNext(new Object());
}
}
})
.subscribeOn(Schedulers.single());
}}
And you should use it before .subscribe like this:
.retryWhen(new RetryWithDelayOrInternet())
You should manually change isInternetUp field
public class InternetConnectionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
boolean networkAvailable = isNetworkAvailable(context);
RetryWithDelayOrInternet.isInternetUp = networkAvailable;
}
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}}
As a previous user said, if your are using Retrofit2 call.clone would suffice, but I also wanted to add a quick example on how that would look:
public class CallbackImpl implements Callback<ResponseBody> {
private final Set<Integer> retryCode = new HashSet<>(Arrays.asList(503, 504));
int requestRetry = 1;
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.code() == 201) {
// Object was created.
} else {
if (requestRetry != 0 && retryCode.contains(response.code())) {
call.clone().enqueue(this);
} else {
// Handle the error
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable) {
if (throwable instanceof IOException) {
// Network failure
} else {
// Conversion Issue
}
}
}
Resilience4j 1.x offered a highly configurable way of defining your retry behaviour and has retrofit adapters as an add-on module. This functionality has since been deprecated in version 2.0.
Example of the available options:
private final Retry retry = Retry.of("id", RetryConfig.<Response<String>>custom()
.maxAttempts(2)
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.code() == 500)
.retryOnException(e -> e instanceof WebServiceException)
.retryExceptions(IOException.class, TimeoutException.class)
.ignoreExceptions(BusinessException.class, OtherBusinessException.class)
.failAfterMaxAttempts(true)
.build());
See the resilience4j documentation on how to integrate that with Retrofit.
It seems it will be present in retrofit 2.0 from the API Spec:
https://github.com/square/retrofit/issues/297.
Currently, the best way seems to be catch exception and retry manually.
As stated in the docs, a better might be to use the baked in authenticators, eg:
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
client.setAuthenticator(new Authenticator() {
#Override public Request authenticate(Proxy proxy, Response response) {
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("jesse", "password1");
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
#Override public Request authenticateProxy(Proxy proxy, Response response) {
return null; // Null indicates no attempt to authenticate.
}
});
Request request = new Request.Builder()
.url("http://publicobject.com/secrets/hellosecret.txt")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
Working prod solution.
public int callAPI() {
return 1; //some method to be retried
}
public int retrylogic() throws InterruptedException, IOException{
int retry = 0;
int status = -1;
boolean delay = false;
do {
if (delay) {
Thread.sleep(2000);
}
try {
status = callAPI();
}
catch (Exception e) {
System.out.println("Error occured");
status = -1;
}
finally {
switch (status) {
case 200:
System.out.println(" **OK**");
return status;
default:
System.out.println(" **unknown response code**.");
break;
}
retry++;
System.out.println("Failed retry " + retry + "/" + 3);
delay = true;
}
}while (retry < 3);
System.out.println("Aborting download of dataset.");
return status;
}

ChannelFutureListener.operationComplete of SslHandler.handshake() Not Being Called on Android

I am using netty-3.6.6 SSL in my Android app. The handshake() is actually done(Android app is able to send/receive data to/from SSL server) but operationComplete never gets called. I need it getting called to perform some tasks.
Anything I missed or did wrong? Thank you.
Follows are the settings and the code piece.
#Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pip = Channels.pipeline();
SSLEngine engine = SslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
SslHandler sslHandler = new SslHandler(engine);
sslHandler.setIssueHandshake(false);
pip.addLast("ssl", sslHandler);
...
return pip;
}
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelConnected");
SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class);
if (sslHandler != null) {
// Begin handshake.
ChannelFuture handshakeFuture = sslHandler.handshake();
handshakeFuture.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
System.out.println("handshake failed(" + future.getCause() + ")");
} else {
System.out.println("handshake OK");
}
}
});
}
}
}
netty might not handle the state of ChannelFuture returned to my application which calls SslHandler.handshake() correctly. I added hsFuture.setSuccess() to SslHandler.handshake() and my operationComplete gets called.
public ChannelFuture handshake() {
...
if (exception == null) { // Began handshake successfully.
try {
final ChannelFuture hsFuture = handshakeFuture;
wrapNonAppData(ctx, channel).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
Throwable cause = future.getCause();
hsFuture.setFailure(cause);
fireExceptionCaught(ctx, cause);
if (closeOnSSLException) {
Channels.close(ctx, future(channel));
}
} else {
hsFuture.setSuccess();
}
}
});
} catch (SSLException e) {

Categories

Resources