适用于Xamarin的Amazon IAP插件 - 使用TaskCompletionSource时崩溃

时间:2017-09-06 20:06:49

标签: xamarin task-parallel-library amazon taskcompletionsource

我正在尝试为Xamarin的Amazon IAP插件实现一个包装器。它以下列方式使用基于事件的系统:

  

您可以启动方法调用并侦听事件。方法调用启动请求,其中一些返回响应。事件是异步系统生成的消息,响应方法调用而发送,以将请求的数据返回给您。   See more here

我的目标是将这个基于事件的系统包装成一些API,允许我将插件与任务一起使用,因此我可以使用async-await语法。为了达到这个目的,我使用了以下示例中的TaskCompletionSource

public async Task<bool> GetProductInfoAsync(params string[] productIds)
{
    var iapService = AmazonIapV2Impl.Instance;
    var tcs = new TaskCompletionSource<bool>();
    var skus = new SkusInput { Skus = productIds.ToList() };
    var requestId = iapService.GetProductData(skus).RequestId;

    GetProductDataResponseDelegator delegator = null;
    delegator = new GetProductDataResponseDelegator(response => 
    {
        if(response.Id == requestId) {
            var result = GetResultFromResponse(response);
            tcs.SetResult(result);
            //iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);
        }
    });

    iapService.AddGetProductDataResponseListener(delegator.responseDelegate);
    return await tcs.Task;
}

如果方法被调用一次,此代码似乎工作正常,但如果连续两次调用它,应用程序立即崩溃,并且打印到控制台的唯一内容是以下消息..

[mono] Unhandled Exception:
[mono] System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

..根本没有任何意义。

那么我有什么明显的遗失吗?或者它可能是插件中的错误?

我已经使用上面的代码创建了一个存储库,因此您可以重现该问题。这是我的游乐场,所以请忽略项目的整个结构,只关注课程AmazonIAPServiceMainActivity

提示1: 注释行//iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);也会导致同一消息崩溃但第一次调用该方法时已经崩溃。

提示2: AmazonIAPService包含一个使用await Task.Delay(TimeSpan.FromMilliseconds(1))的注释方法,并以非常黑客的方式从上面解决问题,我真的不喜欢。

1 个答案:

答案 0 :(得分:0)

问题似乎在于那些功能必须异步运行。在文档中也提到了here。因此,一旦您以某种方式同步运行这些函数,它们就会引发异常,我不知道库中正在发生什么,但是您的hacky解决方案才是真正的解决方案。如果您按如下方式编写函数。也可以。

  PurchaseResponseDelegator delegator = null;
                delegator = new PurchaseResponseDelegator(async response =>
                {
                    await Task.Run(() =>
                    {
                        if (response.RequestId == requestId)
                        {
                            var result = GetPurchaseEventHandler(response);
                            var sucess = taskCompletionSource.TrySetResult(result);        
                            context.RemovePurchaseResponseListener(delegator.responseDelegate);
                        }
                    } );
                });
                // Register for an event
                context.AddPurchaseResponseListener(delegator.responseDelegate);

尽管有async-await解决方案,但我还有一个异常,无论如何,它总是仅对PurchaseUpdates函数的行taskCompletionSource.SetResult(result);抛出异常。如果我改用var sucess = taskCompletionSource.TrySetResult(result);这行,就可以了