我正在尝试为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.
..根本没有任何意义。
那么我有什么明显的遗失吗?或者它可能是插件中的错误?
我已经使用上面的代码创建了一个存储库,因此您可以重现该问题。这是我的游乐场,所以请忽略项目的整个结构,只关注课程AmazonIAPService和MainActivity。
提示1:
注释行//iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);
也会导致同一消息崩溃但第一次调用该方法时已经崩溃。
提示2:
AmazonIAPService
包含一个使用await Task.Delay(TimeSpan.FromMilliseconds(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);
这行,就可以了