Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/258.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中使用async/await和Xamarin或Dot42实现Android回调?_C#_Android_Async Await_Xamarin_Dot42 - Fatal编程技术网

如何在C#中使用async/await和Xamarin或Dot42实现Android回调?

如何在C#中使用async/await和Xamarin或Dot42实现Android回调?,c#,android,async-await,xamarin,dot42,C#,Android,Async Await,Xamarin,Dot42,如何在C#中使用async/await和Xamarin for Android实现回调?这与Android的标准Java编程相比如何?由于Xamarin的Android版本为4.7,在撰写本文时,它仍处于公开的beta版本中,我们可以使用.NET 4.5的功能来实现“异步”方法和对它们的“等待”调用。如果Java中需要任何回调,那么函数中的逻辑代码流就会中断,当回调返回时,您必须在下一个函数中继续代码,这一直困扰着我。考虑这种情况: 我想收集一份Android设备上所有可用TextToSpeec

如何在C#中使用async/await和Xamarin for Android实现回调?这与Android的标准Java编程相比如何?

由于Xamarin的Android版本为4.7,在撰写本文时,它仍处于公开的beta版本中,我们可以使用.NET 4.5的功能来实现“异步”方法和对它们的“等待”调用。如果Java中需要任何回调,那么函数中的逻辑代码流就会中断,当回调返回时,您必须在下一个函数中继续代码,这一直困扰着我。考虑这种情况:

我想收集一份Android设备上所有可用TextToSpeech引擎的列表,然后询问每个引擎安装了哪些语言。我编写的小“TTS设置”活动向用户显示了两个选择框(“微调器”),其中一个列出了此设备上所有TTS引擎支持的所有语言。下面的另一个框列出了第一个框中所选语言的所有可用语音,同样来自所有可用的TTS引擎

理想情况下,此活动的所有初始化都应该在一个函数中进行,例如在onCreate()中。标准Java编程不可能,因为:

这需要两个“中断性”回调—首先初始化TTS引擎—只有在回调onInit()时,它才能完全运行。然后,当我们有一个初始化的TTS对象时,我们需要向它发送一个“android.speech.TTS.engine.CHECK_TTS_DATA”意图,并在ActivityResult()上的活动回调中再次等待它的结果。逻辑流程的另一个中断。如果我们在一个可用TTS引擎列表中进行迭代,那么即使此迭代的循环计数器也不能是单个函数中的局部变量,而是必须成为私有类成员。在我看来相当混乱

下面我将尝试概述实现这一点所需的Java代码

收集所有TTS引擎及其支持的语音的凌乱Java代码 .configureAwait(this)扩展名(加上activity OnCreate()中用于设置内容的另一个代码行)确保您的“this”对象仍然有效,在您从wait返回时指向活动的当前实例,即使发生配置更改。我认为至少应该意识到这一困难,当您开始在Android UI代码中使用async/await时,请参阅Dot42博客上的更多内容:

更新Dot42崩溃
我所经历的异步/等待崩溃现在在Dot42中得到了修复,效果非常好。实际上,比Xamarin代码更好,因为Dot42在活动销毁/重新创建周期之间智能处理了“this”对象。我上面所有的C#代码都应该更新,以考虑到这样的循环,目前在Xamarin中不可能,只有在Dot42中。我将根据其他SO成员的要求更新该代码,目前这篇文章似乎没有得到太多关注。

我使用以下模型将回调转换为异步:

SemaphoreSlim ss = new SemaphoreSlim(0);
int result = -1;

public async Task Method() {
    MethodWhichResultsInCallBack()
    await ss.WaitAsync(10000);    // Timeout prevents deadlock on failed cb
    lock(ss) {
         // do something with result
    }
}

public void CallBack(int _result) {
    lock(ss) {
        result = _result;
        ss.Release();
    }
}
这非常灵活,可用于活动、回调对象内部等


小心,使用错误的方法会造成死锁。如果超时超时,锁将防止在超时后更改结果

wait Task.Run(委托{Event1.WaitOne();})TaskCompletionSource
。而且所有那些空的
catch
es也不是一个好的做法。太好了,谢谢你,@svick!将试用TaskCompetionSource并改进我的代码,而我的免费Xamarin试用将持续。我还在学习C#、Java和Android,所以所有的评论都对我有帮助。空捕获-在某些地方,我真的不在乎有缺陷的TTS引擎(毕竟是第三方代码)是否崩溃,并且仍然想继续我的循环。但你是对的,至少将异常输出到调试日志中会更好,以便了解发生了什么。@svick-希望能提供一个快速的代码示例,演示如何在应用程序的上下文中使用TaskCompletionSource,等待回调。请注意,在等待回调返回时,我们无法阻止当前线程。谢谢@格雷克。只需查看TCS的文档。老实说,这是一门非常简单的课;只要看一下它的API就足以知道如何使用它。@GregK。您可以使用TCS类创建一个任务,该任务表示事件准备就绪的时间,但不会阻塞线程。然后你可以等待那项任务。然后,您只需设置TCS的结果,而不是设置自动重置事件。这就是斯维克的建议。这是使用
任务
模型发出信号的适当方式。使用自动重置事件是为非异步阻塞而设计的。
// Base class for an activity to create an initialized TextToSpeech
// object asynchronously, and starting intents for result asynchronously,
// awaiting their result. Could be used for other purposes too, remove TTS
// stuff if you only need StartActivityForResultAsync(), or add other
// async operations in a similar manner.
public class TtsAsyncActivity : Activity, TextToSpeech.IOnInitListener
{
    protected const String TAG = "TtsSetup";
    private int _requestWanted = 0;
    private TaskCompletionSource<Java.Lang.Object> _tcs;

    // Creates TTS object and waits until it's initialized. Returns initialized object,
    // or null if error.
    protected async Task<TextToSpeech> CreateTtsAsync(Context context, String engName)
    {
        _tcs = new TaskCompletionSource<Java.Lang.Object>();
        var tts = new TextToSpeech(context, this, engName);
        if ((int)await _tcs.Task != (int)OperationResult.Success)
        {
            Log.Debug(TAG, "Engine: " + engName + " failed to initialize.");
            tts = null;
        }
        _tcs = null;
        return tts;
    }

    // Starts activity for results and waits for this result. Calling function may
    // inspect _lastData private member to get this result, or null if any error.
    // For sure, it could be written better to avoid class-wide _lastData member...
    protected async Task<Intent> StartActivityForResultAsync(Intent intent, int requestCode)
    {
        Intent data = null;
        try
        {
            _tcs = new TaskCompletionSource<Java.Lang.Object>();
            _requestWanted = requestCode;
            StartActivityForResult(intent, requestCode);
            // possible exceptions: ActivityNotFoundException, also got SecurityException from com.turboled
            data = (Intent) await _tcs.Task;
        }
        catch (Exception e)
        {
            Log.Debug(TAG, "StartActivityForResult() exception: " + e);
        }
        _tcs = null;
        return data;
    }

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);
        if (requestCode == _requestWanted)
        {
            _tcs.SetResult(data);
        }
    }

    void TextToSpeech.IOnInitListener.OnInit(OperationResult status)
    {
        Log.Debug(TAG, "OnInit() status = " + status);
        _tcs.SetResult(new Java.Lang.Integer((int)status));
    }

}
// Method of public class TestVoiceAsync : TtsAsyncActivity
private async void GetEnginesAndLangsAsync()
{
    _tts = new TextToSpeech(this, null);
    IList<TextToSpeech.EngineInfo> engines = _tts.Engines;
    try
    {
        _tts.Shutdown();
    }
    catch { /* don't care */ }

    foreach (TextToSpeech.EngineInfo ei in engines)
    {
        Log.Debug(TAG, "Trying to create TTS Engine: " + ei.Name);
        _tts = await CreateTtsAsync(this, ei.Name);
        // DISRUPTION 1 from Java code eliminated, we simply await TTS engine initialization here.
        if (_tts != null)
        {
            var el = new EngLang(ei);
            _allEngines.Add(el);
            Log.Debug(TAG, "Engine: " + ei.Name + " initialized correctly.");
            var intent = new Intent(TextToSpeech.Engine.ActionCheckTtsData);
            intent = intent.SetPackage(el.Ei.Name);
            Intent data = await StartActivityForResultAsync(intent, LANG_REQUEST);
            // DISTRUPTION 2 from Java code eliminated, we simply await until the result returns.
            try
            {
                // don't care if lastData or voices comes out null, just catch exception and continue
                IList<String> voices = data.GetStringArrayListExtra(TextToSpeech.Engine.ExtraAvailableVoices);
                Log.Debug(TAG, "Listing voices for " + el.Name() + " (" + el.Label() + "):");
                foreach (String s in voices)
                {
                    el.AddVoice(s);
                    Log.Debug(TAG, "- " + s);
                }
            }
            catch (Exception e)
            {
                Log.Debug(TAG, "Engine " + el.Name() + " listing voices exception: " + e);
            }
            try
            {
                _tts.Shutdown();
            }
            catch { /* don't care */ }
            _tts = null;
        }
    }
    // At this point we have all the data needed to initialize our language
    // and voice selector spinners, can complete the activity setup.
    ...
}
var data = await webClient
             .DownloadDataTaskAsync(myImageUrl)
             .ConfigureAwait(this);
SemaphoreSlim ss = new SemaphoreSlim(0);
int result = -1;

public async Task Method() {
    MethodWhichResultsInCallBack()
    await ss.WaitAsync(10000);    // Timeout prevents deadlock on failed cb
    lock(ss) {
         // do something with result
    }
}

public void CallBack(int _result) {
    lock(ss) {
        result = _result;
        ss.Release();
    }
}