专门用于:
return tcsInitialize.Task;
在这个班上
internal class TextToSpeechImplementation
{
AndroidTextToSpeech tts;
TaskCompletionSource<bool> tcsInitialize;
Task<bool> Initialize()
{
if (tcsInitialize != null && tts != null)
return tcsInitialize.Task;
tcsInitialize = new TaskCompletionSource<bool>();
try
{
tts = new AndroidTextToSpeech(MainActivity.Instance, this);
}
catch (Exception e)
{
tcsInitialize.SetException(e);
}
return tcsInitialize.Task;
}
是否应返回布尔值true或false?
以下是完整的类代码供参考:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Android.OS;
using Android.Speech.Tts;
using AndroidTextToSpeech = Android.Speech.Tts.TextToSpeech;
using Debug = System.Diagnostics.Debug;
using JavaLocale = Java.Util.Locale;
namespace Xamarin.Essentials
{
public static partial class TextToSpeech
{
const int maxSpeechInputLengthDefault = 4000;
static WeakReference<TextToSpeechImplementation> textToSpeechRef = null;
static TextToSpeechImplementation GetTextToSpeech()
{
if (textToSpeechRef == null || !textToSpeechRef.TryGetTarget(out var tts))
{
tts = new TextToSpeechImplementation();
textToSpeechRef = new WeakReference<TextToSpeechImplementation>(tts);
}
return tts;
}
internal static Task PlatformSpeakAsync(string text, SpeechOptions options, CancellationToken cancelToken = default)
{
var textToSpeech = GetTextToSpeech();
if (textToSpeech == null)
throw new PlatformNotSupportedException("Unable to start text-to-speech engine, not supported on device.");
var max = maxSpeechInputLengthDefault;
if (Platform.HasApiLevel(BuildVersionCodes.JellyBeanMr2))
max = AndroidTextToSpeech.MaxSpeechInputLength;
return textToSpeech.SpeakAsync(text, max, options, cancelToken);
}
internal static Task<IEnumerable<Locale>> PlatformGetLocalesAsync()
{
var textToSpeech = GetTextToSpeech();
if (textToSpeech == null)
throw new PlatformNotSupportedException("Unable to start text-to-speech engine, not supported on device.");
return textToSpeech.GetLocalesAsync();
}
}
internal class TextToSpeechImplementation : Java.Lang.Object, AndroidTextToSpeech.IOnInitListener,
#pragma warning disable CS0618
AndroidTextToSpeech.IOnUtteranceCompletedListener
#pragma warning restore CS0618
{
AndroidTextToSpeech tts;
TaskCompletionSource<bool> tcsInitialize;
TaskCompletionSource<bool> tcsUtterances;
Task<bool> Initialize()
{
if (tcsInitialize != null && tts != null)
return tcsInitialize.Task;
tcsInitialize = new TaskCompletionSource<bool>();
try
{
// set up the TextToSpeech object
tts = new AndroidTextToSpeech(Platform.AppContext, this);
#pragma warning disable CS0618
tts.SetOnUtteranceCompletedListener(this);
#pragma warning restore CS0618
}
catch (Exception e)
{
tcsInitialize.SetException(e);
}
return tcsInitialize.Task;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
tts?.Stop();
tts?.Shutdown();
tts = null;
}
base.Dispose(disposing);
}
int numExpectedUtterances = 0;
int numCompletedUtterances = 0;
public async Task SpeakAsync(string text, int max, SpeechOptions options, CancellationToken cancelToken)
{
await Initialize();
// Wait for any previous calls to finish up
if (tcsUtterances?.Task != null)
await tcsUtterances.Task;
if (cancelToken != null)
{
cancelToken.Register(() =>
{
try
{
tts?.Stop();
tcsUtterances?.TrySetResult(true);
}
catch
{
}
});
}
if (options?.Locale?.Language != null)
{
JavaLocale locale = null;
if (!string.IsNullOrWhiteSpace(options?.Locale.Country))
locale = new JavaLocale(options.Locale.Language, options.Locale.Country);
else
locale = new JavaLocale(options.Locale.Language);
tts.SetLanguage(locale);
}
else
{
SetDefaultLanguage();
}
if (options?.Pitch.HasValue ?? false)
tts.SetPitch(options.Pitch.Value);
else
tts.SetPitch(TextToSpeech.PitchDefault);
var parts = text.SplitSpeak(max);
numExpectedUtterances = parts.Count;
tcsUtterances = new TaskCompletionSource<bool>();
var guid = Guid.NewGuid().ToString();
for (var i = 0; i < parts.Count && !cancelToken.IsCancellationRequested; i++)
{
// We require the utterance id to be set if we want the completed listener to fire
var map = new Dictionary<string, string>
{
{ AndroidTextToSpeech.Engine.KeyParamUtteranceId, $"{guid}.{i}" }
};
if (options != null && options.Volume.HasValue)
map.Add(AndroidTextToSpeech.Engine.KeyParamVolume, options.Volume.Value.ToString(CultureInfo.InvariantCulture));
// We use an obsolete overload here so it works on older API levels at runtime
// Flush on first entry and add (to not flush our own previous) subsequent entries
#pragma warning disable CS0618
tts.Speak(parts[i], i == 0 ? QueueMode.Flush : QueueMode.Add, map);
#pragma warning restore CS0618
}
await tcsUtterances.Task;
}
public void OnInit(OperationResult status)
{
if (status.Equals(OperationResult.Success))
tcsInitialize.TrySetResult(true);
else
tcsInitialize.TrySetException(new ArgumentException("Failed to initialize Text to Speech engine."));
}
public async Task<IEnumerable<Locale>> GetLocalesAsync()
{
await Initialize();
if (Platform.HasApiLevel(BuildVersionCodes.Lollipop))
{
try
{
return tts.AvailableLanguages.Select(a => new Locale(a.Language, a.Country, a.DisplayName, string.Empty));
}
catch (Exception ex)
{
Debug.WriteLine("Unable to query language on new API, attempting older api: " + ex);
}
}
return JavaLocale.GetAvailableLocales()
.Where(IsLocaleAvailable)
.Select(l => new Locale(l.Language, l.Country, l.DisplayName, string.Empty))
.GroupBy(c => c.ToString())
.Select(g => g.First());
}
private bool IsLocaleAvailable(JavaLocale l)
{
try
{
var r = tts.IsLanguageAvailable(l);
return
r == LanguageAvailableResult.Available ||
r == LanguageAvailableResult.CountryAvailable ||
r == LanguageAvailableResult.CountryVarAvailable;
}
catch (Exception ex)
{
Debug.WriteLine("Error checking language; " + l + " " + ex);
}
return false;
}
public void OnUtteranceCompleted(string utteranceId)
{
numCompletedUtterances++;
if (numCompletedUtterances >= numExpectedUtterances)
tcsUtterances?.TrySetResult(true);
}
#pragma warning disable 0618
void SetDefaultLanguage()
{
if (Platform.HasApiLevel(BuildVersionCodes.JellyBeanMr2))
{
try
{
if (tts.DefaultLanguage == null && tts.Language != null)
tts.SetLanguage(tts.Language);
else if (tts.DefaultLanguage != null)
tts.SetLanguage(tts.DefaultLanguage);
}
catch
{
if (tts.Language != null)
tts.SetLanguage(tts.Language);
}
}
else
{
if (tts.Language != null)
tts.SetLanguage(tts.Language);
}
}
#pragma warning restore 0618
}
}
答案 0 :(得分:4)
通常,您无法显式完成/取消Task<TResult>
,因为其SetResult
/ etc。成员不公开。但是TaskCompletionSource<TResult>
向您展示了这些功能。因此,无论何时需要返回必须“手动”完成的任务或发生其他事件时,都可以使用完成源。
考虑以下伪示例:
private TaskCompletionSource<int> wheelOfFortuneCompletion;
private Task<int> GiveSomeNumberAsync()
{
StartWheelOfFortuneAnimation();
wheelOfFortuneCompletion = new TaskCompletionSource<int>();
return wheelOfFortuneCompletion.Task; // this is an uncompleted task
}
private void WheelStopped(object sender, WheelOfFortuneEventArgs e)
{
// the result of GiveSomeNumberAsync will be completed when this line is executed:
wheelOfFortuneCompletion.SetResult(e.Value);
}
private void WheelBroken(object sender, WheelOfFortuneEventArgs e)
{
wheelOfFortuneCompletion.SetException(new InvalidOperationException("Oops"));
}
在您的原始示例中,tcsInitialize
用OnInit
方法完成。在调用该方法之前,Initialize
方法的返回任务是“正在运行”。
答案 1 :(得分:2)
对于TaskCompletionSource<bool>
,tcsInitialize.Task;
将返回Task<bool>
,它适合该函数的签名。