I'm trying to develop a mobile app with a SpeechToText feature, I found an example here Original Post and i tried to follow its steps, but when i run the application and i tap on the button to record, i get a message saying "Unhandled Exception occurr, No body on method..".
I tried to debug and what I get is that its something related to the DependecyService running the SpeechToTextAsync method from the ISpeecehToText interface.
Now I dont use interfaces too much so i'm a bit stuck understanding what is causing this error and how to solve it.
namespace LiveScoring {
public partial class MainPage : ContentPage {
public MainPage() {
InitializeComponent();
}
public void RecordBtn_Clicked(object sender, EventArgs e) {
WaitForSpeechToText();
}
private async void WaitForSpeechToText() {
Output_lbl.Text = await DependencyService.Get<ISpeechToText>().SpeechToTextAsync();
>> here I get the error
}
}
}
using System.Threading.Tasks;
namespace LiveScoring {
public interface ISpeechToText {
Task<string> SpeechToTextAsync();
}
}
namespace LiveScoring.Droid {
public class SpeechToText : ISpeechToText {
private const int VOICE = 10;
public static string SpeechText;
public static AutoResetEvent autoEvent = new AutoResetEvent(false);
public SpeechToText() { }
public async Task<string> SpeechToTextAsync() {
var tcs = new TaskCompletionSource<string>();
try {
var voiceIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);
voiceIntent.PutExtra(RecognizerIntent.ExtraPrompt, "Talk now");
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 15000);
voiceIntent.PutExtra(RecognizerIntent.ExtraMaxResults, 1);
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguage, Java.Util.Locale.Default);
SpeechText = "";
autoEvent.Reset();
try {
((Activity)Forms.Context).StartActivityForResult(voiceIntent, VOICE);
} catch (ActivityNotFoundException a) {
tcs.SetResult("Device doesn't support speech to text");
}
await Task.Run(() => { autoEvent.WaitOne(new TimeSpan(0, 2, 0)); });
return SpeechText;
} catch (Exception ex) {
tcs.SetException(ex);
}
return "";
}
}
}
Try to add this above your namespace LiveScoring.Droid { line, ie:
[assembly: Dependency(typeof(SpeechToText))]
namespace LiveScoring.Droid {
...
}
This way it will register the dependency service, so it will be called when you use the DependencyService.Get<>() method.
Related
I have a problem which is very similar to this one. However, in my case I create a ReactiveCommand that calls an async method on execution. The ThrownExceptions observable doesn't seem to pipe any exceptions no matter where they are thrown (directly in the method or in the task started by it).
I have a written a minimum example to demonstrate that. I know that ThrownExceptions doesn't catch everything but I don't know for which cases it is not designed to work or how to handle these exceptions correctly.
using ReactiveUI;
using System;
using System.Reactive;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static void Main()
{
var test = new TestClass();
while (Console.ReadKey().Key == ConsoleKey.Enter)
{
test.Command.Execute().Subscribe();
}
Console.ReadKey();
}
}
public class TestClass
{
public TestClass()
{
Command = ReactiveCommand.Create(() => RunCommand());
Command.ThrownExceptions.Subscribe(ex => HandleException(ex));
}
public ReactiveCommand<Unit, Task> Command { get; private set; }
private async Task RunCommand()
{
//throw new Exception("will not be handled");
//await Task.Run(() => throw new Exception("will also not be handled"));
}
private void HandleException(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
To answer my own question: commands that call async methods have to be created using the CreateFromTask method instead of the Create method.
In addition, the exceptions are not caught if the command is executed by subscribing to it's Execute observable. The Execute method of the ICommand interface has to be used instead (commands should be exposed to the public using this interface anyway).
I have changed my demo project as below:
class Program
{
static void Main()
{
var test = new TestClass();
while (Console.ReadKey().Key == ConsoleKey.Enter)
{
(test.Command as ICommand).Execute(null);
}
Console.ReadKey();
}
}
public class TestClass
{
public TestClass()
{
Command = ReactiveCommand.CreateFromTask(RunCommand);
Command.ThrownExceptions.Subscribe(ex => HandleException(ex));
}
public ReactiveCommand<Unit, Unit> Command { get; private set; }
private async Task RunCommand()
{
//throw new Exception("will be handled");
await Task.Run(() =>
{
throw new Exception("will also be handled");
});
}
private void HandleException(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
In my Prism module in ViewModel class in OnNavigatedTo method
I would like to fill an ObservableCollection with results of multiple async calls without waiting for all calls to complete.
I am using answer from this question:
How to hydrate a Dictionary with the results of async calls?
The following code is a cleaned-up version of my real code:
My status class:
public class Status
{
public string ipAddress;
public string status;
}
My view model:
using Prism.Mvvm;
using Prism.Regions;
public class StatusViewModel : BindableBase, INavigationAware
{
ObservableCollection<Status> statusCollection = new ObservableCollection<Status>();
HttpClient httpClient = new HttpClient();
Ping ping = new Ping();
List<string> ipAddressList = new List<string>();
public void OnNavigatedTo(NavigationContext navigationContext)
{
GetEveryStatus();
}
public void GetEveryIp() // this is not important, works ok
{
var addressBytes = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).GetAddressBytes();
for (byte i = 1; i < 255; ++i)
{
addressBytes[3] = i;
string ipAddress = new IPAddress(addressBytes).ToString();
if (ping.Send(ipAddress, 10).Status == IPStatus.Success)
{
ipAddressList.Add(ipAddress);
}
}
}
public void GetEveryStatus() // this is important, here is the problem
{
GetEveryIp();
var task = GetStatusArray(ipAddressList);
statusCollection.AddRange(task.Result);
}
// solution from stackoverflow, but it throws exception
public async Task<Status[]> GetStatusArray(List<string> ipAddressList)
{
Status[] statusArray = await Task.WhenAll(
ipAddressList.Select(
async ipAddress => new Status(
ipAddress,
await httpClient.GetStringAsync("http://" + ipAddress + ":8080" + "/status")
)
)
);
return statusArray;
}
}
but that didn't work because GetStringAsync can throw an exception, so I changed it to this:
public void GetEveryStatus() // this is important, here is the problem
{
GetEveryIp();
foreach (string ipAddress in ipAddressList)
{
try
{
var task = httpClient.GetStringAsync("http://" + ipAddress + ":8080" + "/status");
statusCollection.Add(new Status(ipAddress, task.Result));
}
catch (Exception)
{
}
}
}
but it still doesn't work.
What is the right way to do this? Thank you!
Thanks to #AccessDenied for explaining the role of async in interface implementation.
Thanks to #Selvin for explaining Task.Result and Task.Wait.
If anyone is interesed in the final solution, here it is:
PositioningModule is a hardware device, this class has nothing to do with Prism.Modularity.IModule
public class PositioningModule
{
public string IpAddress { get; set; }
public PositioningModuleStatus PositioningModuleStatus { get; set; }
public PositioningModule(string ipAddress, PositioningModuleStatus positioningModuleStatus)
{
IpAddress = ipAddress;
PositioningModuleStatus = positioningModuleStatus;
}
}
The ViewModel:
I had to use BindingOperations.EnableCollectionSynchronization and lock on the ObservableCollection. This is the main reason why it didn't work async before!
Changing OnNavigatedTo to async blocked the UI, so I used Task.Run().
using Prism.Mvvm;
using Prism.Regions;
public class DomePositioningViewModel : BindableBase, INavigationAware
{
ObservableCollection<PositioningModule> _positioningModuleCollection = new ObservableCollection<PositioningModule>();
readonly object _lock = new object();
DomePositioningModel _domePositioningModel = new DomePositioningModel();
public DomePositioningViewModel()
{
BindingOperations.EnableCollectionSynchronization(_positioningModuleCollection, _lock);
}
public /* async */ void OnNavigatedTo(NavigationContext navigationContext)
{
//await _domePositioningModel.ScanForModulesAsync(AddModule); - this blocks the UI
Task.Run(() => _domePositioningModel.ScanForModulesAsync(AddModule));
}
private void AddModule(PositioningModule module)
{
lock (_lock)
{
_positioningModuleCollection.Add(module);
}
}
}
The Model:
I changed Send to SendPingAsync and I had to use new Ping() instead of ping.
Using Select instead of foreach to make the calls parallel made everything much faster!
#define PARALLEL
public class DomePositioningModel
{
private readonly HttpClient _httpClient = new HttpClient();
public DomePositioningModel()
{
_httpClient.Timeout = TimeSpan.FromMilliseconds(50);
}
public async Task ScanForModulesAsync(Action<PositioningModule> AddModule)
{
List<string> ipAddressList = new List<string>();
var addressBytes = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).GetAddressBytes();
for (addressBytes[3] = 1; addressBytes[3] < 255; ++addressBytes[3])
{
ipAddressList.Add(new IPAddress(addressBytes).ToString());
}
//Ping ping = new Ping(); - this behaves strangely, use "new Ping()" instead of "ping"
#if PARALLEL
var tasks = ipAddressList.Select(async ipAddress => // much faster
#else
foreach (string ipAddress in ipAddressList) // much slower
#endif
{
PingReply pingReply = await new Ping().SendPingAsync(ipAddress, 10); // use "new Ping()" instead of "ping"
if (pingReply.Status == IPStatus.Success)
{
try
{
string status = await _httpClient.GetStringAsync("http://" + ipAddress + ":8080" + "/status");
if (Enum.TryParse(status, true, out PositioningModuleStatus positioningModuleStatus))
{
AddModule?.Invoke(new PositioningModule(ipAddress, positioningModuleStatus));
}
}
catch (TaskCanceledException) // timeout
{
}
catch (HttpRequestException) // could not reach IP
{
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
}
#if PARALLEL
);
await Task.WhenAll(tasks);
#endif
}
}
It didn't benchmark it because the difference is so obvious - about 0.5 sec instead of 14 sec!
I can't seem to get my code work, although I tried several different approaches. Here is my preferred code snippet:
var client = await ApiClientProvider.GetApiClient();
var freeToPlayChampions = await client.GetChampionsAsync(true, Region);
var championsData = freeToPlayChampions.Select(x =>
client.GetStaticChampionByIdAsync(
(int)x.Id,
platformId: Region));
ConsoleTable.From(await Task.WhenAll(championsData)).Write();
When debugging I see that the code hangs on await Task.WhenAll(championsData). So i tried to make the code more easy:
var client = await ApiClientProvider.GetApiClient();
var freeToPlayChampions = await client.GetChampionsAsync(true, Region);
var table = new ConsoleTable();
foreach(var freeToPlayChampion in freeToPlayChampions)
{
var championsData = client.GetStaticChampionByIdAsync(
(int)freeToPlayChampion.Id,
platformId: Region);
table.AddRow(await championsData);
}
table.Write();
Unfortunately this hangs, as well. Again on the same code part, e.g. await championsData.
How can this 'easy' usage of async/await lead to an deadlock? Thanks in advance for help!
EDIT:
Here is the whole class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ConsoleTables;
using Mono.Options;
using RiotNet.Models;
using RiotShell.Properties;
namespace RiotShell
{
public class FreeToPlay : IShellCommand
{
public IEnumerable<string> Names { get; }
public OptionSet Options { get; }
public bool ShowHelp { get; private set; }
public string Region { get; private set; }
public FreeToPlay()
{
Names = new List<string>
{
"freetoplay",
"ftp"
};
Options = new OptionSet
{
{ "r|region=" , "The region to execute against", x => Region = x},
{ "h|help|?" , "Show help", x => ShowHelp = true }
};
}
public async Task Execute(IEnumerable<string> args)
{
if (ShowHelp)
{
Options.WriteOptionDescriptions(Console.Out);
return;
}
if (args.Any())
{
throw new Exception(Resources.TooManyArgumentsProvided);
}
if (Region == null)
{
throw new Exception(string.Format(Resources.RequiredOptionNotFound, "region"));
}
if (!PlatformId.All.Contains(Region))
{
throw new Exception(string.Format(Resources.InvalidRegion, Region));
}
var client = await ApiClientProvider.GetApiClient();
var freeToPlayChampions = await client.GetChampionsAsync(true, Region);
var championsData = freeToPlayChampions.Select(x =>
client.GetStaticChampionByIdAsync(
(int)x.Id,
platformId: Region));
ConsoleTable.From(await Task.WhenAll(championsData)).Write();
}
}
}
And here is the caller code, my main method:
using System;
using System.Threading.Tasks;
using RiotShell.Properties;
namespace RiotShell
{
public class Program
{
public static async Task Main()
{
while (true)
{
Console.Write(Resources.RiotShellLineString);
var input = Console.ReadLine();
try
{
var parsedArgs = InputParser.Parse(input);
(var command, var commandArgs) = ArgsToIShellCommandCaster.GetCommand(parsedArgs);
await command.Execute(commandArgs);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}
Since it was wished, here the code for the ApiProvider:
using RiotNet;
using System.Threading.Tasks;
namespace RiotShell
{
public class ApiClientProvider
{
private static IRiotClient _client;
public static async Task<IRiotClient> GetApiClient()
{
if (_client != null)
{
_client.Settings.ApiKey = await KeyService.GetKey();
return _client;
}
_client = new RiotClient(new RiotClientSettings
{
ApiKey = await KeyService.GetKey()
});
return _client;
}
}
}
Here's my entire code. I think the test should pass, but it fails. I've (unsuccessfully) tried using some of the overloads to Consumer.
using MassTransit;
using NUnit.Framework;
using System.Threading.Tasks;
namespace MassTransitTests
{
public class Message
{
}
public class MessageConsumer : IConsumer<Message>
{
public static int ConsumedCount
{
get;
private set;
}
public Task Consume(ConsumeContext<Message> context)
{
ConsumedCount++;
return Task.FromResult(0);
}
}
[TestFixture]
public class MassTransitTest
{
[Test]
public async Task BasicTestAsync()
{
// Arrange
var control = Bus.Factory.CreateUsingInMemory(configure =>
{
configure.ReceiveEndpoint("myQueue", endpoint =>
{
endpoint.Consumer<MessageConsumer>();
});
});
// Act
using (var handle = control.Start())
{
await control.Publish(new Message());
await control.Publish(new Message());
}
// Assert
Assert.That(MessageConsumer.ConsumedCount, Is.EqualTo(2));
}
}
}
Their documentation shows this, which is what I'm doing:
var busControl = Bus.Factory.CreateUsingInMemory(cfg =>
{
cfg.ReceiveEndpoint("queue_name", ep =>
{
//configure the endpoint
})
});
What am I doing wrong/what I do need to change in my Arrange/Act to get my Assert to work?
After digging through their tests, I found what I was missing:
[1] You need* to await BusHandle.Ready, which I wasn't doing. *(The test works without this - at least the first time I ran it, but that may just be a race condition working in my favor....)
[2] The calls to Publish apparently complete whenever the bus has received the message I'm guessing - not when the handlers/consumers of the message have completed their work. Therefore you need to notify the calling code that the handlers have finished if that's what you're testing. Here's one way to do this - use TaskCompletionSource<T> (similar to what I found in their codebase). Obviously I may not have been perfect in my thread-safety and my lock usage is a bit sledge-hammer-esque, but this illustrates the point:
using MassTransit;
using NUnit.Framework;
using System.Threading.Tasks;
namespace MassTransitTests
{
public class Message
{
}
public class MessageConsumer : IConsumer<Message>
{
public static int TargetConsumedCount
{
get { return _targetConsumedCount; }
set
{
lock (_lock)
{
_targetConsumedCount = value;
CheckTargetReached();
}
}
}
private static void CheckTargetReached()
{
if (_consumedCount >= TargetConsumedCount)
{
_targetReached.SetResult(true);
}
}
public static Task<bool> TargetReached { get; private set; }
private static int _consumedCount;
private static int _targetConsumedCount;
private static TaskCompletionSource<bool> _targetReached;
private static object _lock;
static MessageConsumer()
{
_lock = new object();
_targetReached = new TaskCompletionSource<bool>();
TargetReached = _targetReached.Task;
}
public Task Consume(ConsumeContext<Message> context)
{
lock (_lock)
{
_consumedCount++;
CheckTargetReached();
}
return Task.FromResult(0);
}
}
[TestFixture]
public class MassTransitTest
{
[Test]
public async Task BasicTestAsync()
{
// Arrange
var control = Bus.Factory.CreateUsingInMemory(configure =>
{
configure.ReceiveEndpoint("myQueue", endpoint =>
{
endpoint.Consumer<MessageConsumer>();
});
});
using (var handle = control.Start())
{
await handle.Ready; // [1]
// Act
await control.Publish(new Message());
await control.Publish(new Message());
// Assert
MessageConsumer.TargetConsumedCount = 2;
await MessageConsumer.TargetReached; // [2]
}
}
}
}
I have an async service
The service contract defined like:
[ServiceBehavior(InstanceContextMode = InstanceContext.PerCall]
Myservice
My client is defined like:
MyServiceClient task= null;
InstanceContext instanceContext = new InstanceContext(this);
task = new MyServiceClient(instanceContext);
task.MyMethod();
And the client class implements the call back methods (finish, progress etc...).
It's works fine, but if I call to the method, and she start running on the server and I shut down the server,I can't know the status of my call, and the client still think that the methods still running.
So, how can I check if this call is still running?
Thanks for helpers :)
Edit:
CallBack Interface:
public interface IServiceCallback
{
[OperationContract(IsOneWay=true)]
void NotifyFinished();
[OperationContract(IsOneWay=true)]
void NotifyProgress(int x);
[OperationContract(IsOneWay=true)]
void NotifyFailed(Exception exception);
}
Service Interface:
[ServiceContract(CallbackContract = typeof (IServiceCallback)]
public interface IAsyncService
{
[OperationContract(IsOneWay=true)]
void AsyncRunning();
}
Service Class:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class AsyncService : IAsyncService
{
private IServiceCallback ServiceCallback {get; set;}
public void AsyncRunningProxy ()
{
for(int x=0; x<100 ; x++)
{
AsyncService.NotifyProgress(x);
}
}
private void EndMethod(IAsyncResult res)
{
AsyncResult result = (AsyncResult)res;
try
{
((dynamic)result.AsyncDelegate).EndInvoke(res);
AsyncService.NotifyFinished();
}
catch (Exception e)
{
AsyncService.NotifyFailed(e);
}
}
public void AsyncRunning ()
{
ServiceCallback = OperationContext.Current.GetCallBackChannel<IServiceCallback>();
Action action = AsyncRunningProxy;
action.BeginInvoke(EndMethod, null);
}
}
Client Class:
public class ServiceRunner : IServiceCallback
{
private ManualResetEvent reset {get; set;}
public ServiceRunner()
{
reset = new ManualResetEvent(false);
}
public void Run()
{
AsyncServiceClient client = null;
InstanceContext instanceContext = new InstanceContext(this);
client = new AsyncServiceClient(instanceContext);
client.AsyncRunning();
reset.WaitOne();
}
public void NotifyProgress(int x)
{
Console.WriteLine(x);
}
public void NotifyFinished()
{
}
public void NotifyFailed(Exception e)
{
Console.WriteLine(e.Message);
reset.Set();
}
}
Edit: new client Class:
Client Class:
public class ServiceRunner : IServiceCallback
{
private ManualResetEvent reset { get; set; }
private string IsRunning { get; set; }
public ServiceRunner()
{
reset = new ManualResetEvent(false);
IsRunning = true;
}
public void Run()
{
AsyncServiceClient client = null;
InstanceContext instanceContext = new InstanceContext(this);
client = new AsyncServiceClient(instanceContext);
client.AsyncRunning();
new Thread(()=>
{
while(IsRunning)
{
try
{
client.IsAlive();
Thrad.Sleep(60 * 1000);
}
catch (Exception e) // The server is not responding.
{
NotifyFailed(e);
return;
}
}
}).Start();
reset.WaitOne();
}
public void NotifyProgress(int x)
{
Console.WriteLine(x);
}
public void NotifyFinished()
{
IsRunning = false;
reset.Set();
}
public void NotifyFailed(Exception e)
{
IsRunning = false;
Console.WriteLine(e.Message);
reset.Set();
}
}
In order to have more control of your clients request to the service, you should be able to use the inbuilt Task and Async support to monitor and if necessary handle connection delays.
The support for generating Task-based operations on the client side will be available to users who rely on proxies generated by our client generation tools (svcutil.exe or Add Service Reference), as well as to users who prefer to directly use ChannelFactory
The following code provides a rough example:
Task<string> task = new MyServiceClient().MyMethod();
if (task == await Task.WhenAny(task, Task.Delay(1000)))
{
Console.WriteLine(await task);
}
else
{
// handle delay …
}
Refer to the following MSDN blog entry for more information:
http://blogs.msdn.com/b/endpoint/archive/2010/11/13/simplified-asynchronous-programming-model-in-wcf-with-async-await.aspx
Regards,
As #adkSerenity mention you may implement timeout logic, but I guess your question not about that.
The callback method will be(and should be) called in case of exception for example connection lose or internal connection time out.
private static void CallbackSample(IAsyncResult asynchronousResult)
{
try
{
// State of request is asynchronous.
RequestState myRequestState=(RequestState) asynchronousResult.AsyncState;
HttpWebRequest myHttpWebRequest2=myRequestState.request;
myRequestState.response = (HttpWebResponse);
//next line may throw exception
myHttpWebRequest2.EndGetResponse(asynchronousResult);
}
catch(WebException e)
{
}
}
So async communication looks like fire and forget. Your callback method will be called when you get the result(exception too), but if you decide not handle it(custom time out logic) you must "foret" about callback processing. No way to check is alive(except of course custom api).