I have async method ExecuteWithRetryAsync which implements some retry logic and must show ProgressDialog as soon as it is called. Currently the first call to Show() never really works. The progress dialog shows only after the AlertDialog is confirmed (second comment). How do I make Show() at the beginning of ExecuteWithRetryAsync actually show the progress dialog?
public async Task<object> ExecuteWithRetryAsync(string methodName, object[] args)
{
MethodInfo methodInfo = typeof(Service1).GetMethod(methodName);
// below progress dialog not showing
mDialog = new ProgressDialog(context);
mDialog.SetMessage("Bitte warten...");
mDialog.SetCancelable(false);
mDialog.Show();
for (; ; )
{
try
{
object result = null;
try
{
// Call web service.
result = methodInfo?.Invoke(webservice, args);
}
catch (TargetInvocationException tie)
{
if (tie.InnerException != null) throw tie.InnerException;
}
mDialog?.Dismiss();
return result;
}
catch (Exception e)
{
Trace.TraceError("Operation Exception");
currentRetry++;
if (/*currentRetry > RetryCount || */!IsTransient(e))
{
// If this isn't a transient error or we shouldn't retry,
// rethrow the exception.
throw;
}
}
mDialog?.Dismiss();
await DisplayAlert(
context.GetString(Resource.String.timeout),
context.GetString(Resource.String.retry_operation),
context.GetString(Resource.String.Ok),
methodInfo);
// this progress dialog is showing
mDialog = new ProgressDialog(context);
mDialog.SetMessage("Bitte warten...");
mDialog.SetCancelable(false);
mDialog.Show();
await Task.Delay(MaxDelayMilliseconds);
}
}
UPDATE: I observed that when connection of device is disabled it takes about ~10-15 secs for ExecuteWithRetryAsync to start execution and in the meantime device shows app not responding dialog several times, whereas with the connection on it executes immediately. Why that?
UPDATE 2: When I put await Task.Delay (50) after calling Show() it does show, but the progress dialog animation is not updating, it's frozen.
UPDATE 3: I put following line result = methodInfo?.Invoke(Utility.WsHueckmann, args) inside await Task.Run so it becomes await Task.Run(() => { result = methodInfo?.Invoke(Utility.WsHueckmann, args); }) and now it's working and spinner is updating.
The reason behind your progress not spinning is because it is not indeterminate add the following code and it should work
progress.Indeterminate = true;
progress.SetProgressStyle(Android.App.ProgressDialogStyle.Spinner);
Update
Put the following line
result = methodInfo?.Invoke(Utility.WsHueckmann, args)
inside await Task.Run so it becomes
await Task.Run(() => { result = methodInfo?.Invoke(Utility.WsHueckmann, args); })
and now it's working and spinner is updating.
Related
Hi I am using this DeleteObjectsAsync and it doesn't work in a way that it looks like it stops, and it doesn't execute the next line and nothing is deleted.
I added try catch but it isn't going to any of the catches. But if I use the non-async one DeleteObjects, it works but I had to put a .Wait() like this client.DeleteObjects(deleteObjectRequest).Wait() otherwise it stops also like the DeleteObjectsAsync. It stops in a way that the RUN is still on but it just flickers after calling the delete and will not execute the next lines of codes as if it already reached the last line of it. How to make this work? thanks
My code
public static async Task<bool> DeleteFilesFromS3Async(List<KeyVersion> keyVersions, string bucketName = "")
{
try
{
using (var client = new AmazonS3Client(AmazonKey, AmazonSecretKey, AmazonRegion))
{
var deleteObjectRequest = new DeleteObjectsRequest { BucketName = bucketName , Objects = keyVersions };
await client.DeleteObjectsAsync(deleteObjectRequest);
return true;
}
}
catch (DeleteObjectsException e)
{
return false;
}
catch (AmazonS3Exception e)
{
return false;
}
catch (Exception x)
{
return false;
}
}
Make sure you are calling it the right way.
Either, caller method must be async and you must be using await there.
public async Task DeleteFile()
{
var result = await DeleteFilesFromS3Async(...);
}
Or, invoke this method synchronously.
public void DeleteFile()
{
var result = DeleteFilesFromS3Async(...).Result;
}
Currently, this is how I read from C++ using Flutter:
final Uint8List result = await platform.invokeMethod(Common.MESSAGE_METHOD, {"message": buffer});
It is handled by Kotlin like this:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == MESSAGE_METHOD) {
val message: ByteArray? = call.argument<ByteArray>("message")
//... //response = Read something FROM C++
result.success(response)
Since this happens in the main thread, if I take too much time to answer, I make Flutter's UI slow.
Is there a solution to get C++ data in an async way?
I know that Flutter has support for event channels to send data back from C++ to Flutter. But what about just requesting the data on the Flutter side and waiting for it to arrive in a Future, so I can have lots of widgets inside a FutureBuilder that resolves to something when ready?
If reading something from C++ is a heavy process, You can use AsysncTask to perform it in the background for android.
internal class HeavyMsgReader(var result: MethodChannel.Result) : AsyncTask<ByteArray?, Void?, String?>() {
override fun doInBackground(vararg message: ByteArray?): String {
//... //response = Read something FROM C++
return "response"
}
override fun onPostExecute(response: String?) {
result.success(response)
}
}
Calling async task:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == MESSAGE_METHOD) {
val message: ByteArray? = call.argument<ByteArray>("message")
HeavyMsgReader(result).execute(message);
Hopefully this will work
import 'dart:async';
Future<Uint8List> fetchData(buffer) async {
final Uint8List result = await platform.invokeMethod(Common.MESSAGE_METHOD, {"message": buffer});
return result;
}
And just call it, like this
fetchData(buffer).then((result) => {
print(result)
}).catchError(print);
Proof that its working:
import 'dart:async';
Future<String> fetchUserOrder() async {
await Future.delayed(Duration(seconds: 5));
return 'Callback!';
}
Future<void> main() async {
fetchUserOrder().then((result) => {
print(result)
}).catchError(print);
while(true){
print('main_thread_running');
await Future.delayed(Duration(seconds: 1));
}
}
output:
main_thread_running
main_thread_running
main_thread_running
main_thread_running
main_thread_running
Callback!
main_thread_running
main_thread_running
...
I have Ember code where the backend API calls are abstracted into a
separate service. This service uses ember-ajax library for making
backend calls.
This service sets up the common headers, handles the
timeout errors, and 4xx/5xx errors. And anything else like 422
(validation errors) are left to be handled by the calling code.
-
getCustomerProfile (authenticationToken) {
const backendService = this.get('callBackendService');
return backendService.callEndpoint(GET_METHOD,
getCustomerProfileAPI.url,
{'X-Auth-Token': authenticationToken}).then((customerProfileData) => {
if (!backendService.get('didAnybodyWin') && customerProfileData) {
backendService.set('didAnybodyWin', true);
return customerProfileData.profiles[0];
}
}).catch((error) => {
if (isInvalidError(error)) {
if (!backendService.get('didAnybodyWin')) {
backendService.set('didAnybodyWin', true);
backendService.transitionToErrorPage();
return;
}
}
});
}
and the call-backend-service looks like this
callEndpoint (httpVerb, endPoint, headersParameter, data = {},
timeoutInMillisecs = backendCallTimeoutInMilliseconds) {
const headersConst = {
'Content-Type': 'application/vnd.api+json',
'Accept': 'application/vnd.api+json',
'Brand': 'abc'
};
var headers = Ember.assign(headersParameter, headersConst);
var promiseFunctionWrapper;
this.set('didAnybodyWin', false);
if (httpVerb.toUpperCase() === GET_METHOD) {
Ember.Logger.warn('hit GET Method');
promiseFunctionWrapper = () => {
return this.get('ajax').request(endPoint, {headers});
};
} else if (httpVerb.toUpperCase() === POST_METHOD) {
Ember.Logger.warn('hit POST Method');
promiseFunctionWrapper = () => {
return this.get('ajax').post(endPoint, {data: data, headers: headers});
};
}
return RSVP.Promise.race([promiseFunctionWrapper(), this.delay(timeoutInMillisecs).then(() => {
if (!this.get('didAnybodyWin')) {
this.set('didAnybodyWin', true);
Ember.Logger.error('timeout of %s happened when calling the endpoint %s', backendCallTimeoutInMilliseconds, endPoint);
this.transitionToErrorPage();
return;
}
})]).catch((error) => {
if (!this.get('didAnybodyWin')) {
if (isTimeoutError(error)) {
this.set('didAnybodyWin', true);
Ember.Logger.warn('callBackEndService: isTimeoutError(error) condition is true');
this.transitionToErrorPage();
return;
} else if (isAjaxError(error) && !isInvalidError(error)) { //handles all errors except http 422 (inValid request)
this.set('didAnybodyWin', true);
Ember.Logger.warn('callBackEndService: isAjaxError(error) && !isInvalidError(error) [[ non timeout error]] condition is true');
this.transitionToErrorPage();
return;
} else {
throw error; // to be caught by the caller
}
}
});
},
The callEndpoint does a RSVP.Promise.race call to make sure the called backend API comes back before a timeout happens. It runs two promises and whichever resolves first is the one that wins. didAnybodyWin is the flag that guards both the promises from getting executed.
Up to this part is all fine.
But this didAnybodyWin becomes the shared state of this call-backend-service because it has to convey back to the caller whether it ran the default set of then or catch blocks or does it expect the caller to run its then/catch blocks.
The problem is when model() hook is run, I am doing
RSVP.all {
backendAPICall1(),
backendAPICall2(),
backendAPICAll3()
}
This RSVP.all is going to execute all 3 calls one after another, so they will hit the call-backend-service in an interleaved fashion and hence run the risk of stepping over each other (when it comes to the didAnybodyWin shared state).
How can this situation be avoided ?
Is there any other better way for the callee to signal to the caller whether or not its supposed to do something with the returned promise.
I have a vertx handler code where I do an executeBlocking but for it to work I need to put in a Thread.sleep() in order for the code in the blocking code to fully execute to the point that I can check the results.
Is there a better way around this so I don't do a Thread.sleep?
My handler code the following is the portion where I only kept the relevant components.
try (final VertxHttpResponse response = new VertxHttpResponse(context)) {
context.vertx().executeBlocking(
future -> {
...
try {
dispatcher.invokePropagateNotFound(request,
response);
future.complete();
} finally {
...
}
}, false,
res -> {
if (res.failed()) {
context.fail(wae);
} else {
if (!context.response().ended()) {
context.response().end();
}
}
});
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
}
My test and the relevant parts
#Test
public void test(final TestContext testContext) throws Exception {
final Router router = Router.router(rule.vertx());
final SpringJaxRsHandler handler = SpringJaxRsHandler.registerToRouter(router, MyApp.class);
final RoutingContext routingContext = mock(RoutingContext.class);
when(routingContext.currentRoute()).thenReturn(router.get("/api/hello"));
when(routingContext.vertx()).thenReturn(rule.vertx());
final HttpServerRequest serverRequest = mock(HttpServerRequest.class);
when(serverRequest.absoluteURI()).thenReturn("/api/hello");
when(serverRequest.isEnded()).thenReturn(true);
when(serverRequest.method()).thenReturn(HttpMethod.GET);
when(routingContext.request()).thenReturn(serverRequest);
final HttpServerResponse response = mock(HttpServerResponse.class);
when(response.putHeader(anyString(), anyString())).thenReturn(response);
when(response.headers()).thenReturn(new VertxHttpHeaders());
when(routingContext.response()).thenReturn(response);
handler.handle(routingContext);
Thread.sleep(1000);
// fails without the sleep above
verify(response, times(1)).setStatusCode(200);
}
I tried
testContext.assertTrue(routingContext.response().ended());
But that returned false.
I refactored the code a bit so I don't use routingContext directly but the concept is still the same. I use Async in combination of a when->then(Answer) and have the async.complete() be called in the Answer. Once that is done do an async.await() to wait for the thread to finish.
final Async async = testContext.async();
when(response.write(Matchers.any(Buffer.class))).then(invocation -> {
try {
return response;
} finally {
async.complete();
}
});
when(serverRequest.response()).thenReturn(response);
router.accept(serverRequest);
async.await();
assume the code is correct and webservice timeout occurs.
The problem :
The system crashes and can not display the error message.
How to display error message? So I can provide an alternative to user when there is an error?
1)
I add this Class in the project :
public class MyClass
{
public static async Task LogInSuccess()
{
try
{
-- calling a web service here
}
catch (System.Exception _ex)
{
_strErrorMsg = _ex.InnerException.Message;
throw new Exception("LogInSuccess() " + _strErrorMsg);
}
}
}
--- In the MainPage,
2)
private async void SetUp ()
{
-- code for doing setUp task--
CallWebSvc();
}
3)
private void CallWebSvc()
{
bool ShowError = false;
System.Exception MyException = new Exception();
try
{
-- calling a web service thru the MyClass
System.Threading.Tasks.Task _blnLogInSuccess = MyClass.LogInSuccess();
await _blnLogInSuccess;
if (_blnLogInSuccess.IsCompleted)
{
g_blnLoginStatus = _blnLogInSuccess.Result;
}
}
catch (System.Exception _ex)
{
ShowError = true;
MyException = ex;
}
if (ShowError)
{
var MyMessageBox = new Windows.UI.Popups.MessageDialog("Remote Login Error:" + MyException.Message, "Start Login" );
await MyMessageBox.ShowAsync();
}
}
I assume your CallWebSvc method is async void (as, without async you cannot perform an await) If this is the case, you need to know async void doesn't do the same treatament to exceptions as async task. they aren't catched correctly. If you change your CallWebSvc from async void to async Task, you are going to receive the exception correctly.