如何在Xamarin.Android中异步实现Android应用内计费客户端
我正试图用c#实现下面提到Android的java代码如何在Xamarin.Android中异步实现Android应用内计费客户端,xamarin,xamarin.android,in-app-billing,Xamarin,Xamarin.android,In App Billing,我正试图用c#实现下面提到Android的java代码 List skuList=newarraylist(); skuList.添加(“特优升级”); skuList。添加(“气体”); SkuDetailsParams.Builder params=SkuDetailsParams.newBuilder(); 参数setskulist(skuList).setType(SkuType.INAPP); billingClient.querySkuDetailsAsync(params.buil
List skuList=newarraylist();
skuList.添加(“特优升级”);
skuList。添加(“气体”);
SkuDetailsParams.Builder params=SkuDetailsParams.newBuilder();
参数setskulist(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
新SkuDetailsResponseListener(){
@凌驾
公共无效详细信息响应(BillingResult BillingResult,
列表(详细信息列表){
//处理结果。
}
});
我有两个问题。我想我会在一个单独的线程上运行这段代码,而不是像下面这样的UI线程,以在网络连接完成时保持我的UI响应。这是正确的方法吗?QuerySkuDetailsAsync称为异步,但不作为异步实现。这应该是如何工作的,以及如何在c#中处理,因为它将触发并忘记,但侦听器需要处理响应
public async Task<List<InAppBillingProduct>> GetProductsAsync(List<string> ProductIds)
{
var getSkuDetailsTask = Task.Factory.StartNew(() =>
{
var prms = SkuDetailsParams.NewBuilder();
var type = BillingClient.SkuType.Inapp;
prms.SetSkusList(ProductIds).SetType(type);
BillingClient.QuerySkuDetailsAsync(prms.Build(), new SkuDetailsResponseListener());
return InAppBillingProducts;
});
return await getSkuDetailsTask;
}
公共异步任务GetProductsAsync(列出ProductID)
{
var getsku detailstask=Task.Factory.StartNew(()=>
{
var prms=skudailsparams.NewBuilder();
var type=BillingClient.SkuType.Inapp;
prms.setskulist(productid).SetType(type);
BillingClient.QuerySkuDetailsAsync(prms.Build(),new SkuDetailsResponseListener());
退货不适用产品;
});
返回等待getskutailstask;
}
关于如何处理听众的第二个问题,如下所示。如何从侦听器返回值。我需要InAppBillingProduct对象的返回列表
public class SkuDetailsResponseListener : Java.Lang.Object, ISkuDetailsResponseListener
{
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skus)
{
if (billingResult.ResponseCode == BillingResponseCode.Ok)
{
// get list of Products here and return
}
}
}
公共类SkuDetailsResponseListener:Java.Lang.Object,ISkuDetailsResponseListener
{
公共无效详细信息响应(BillingResult BillingResult、IList SKU)
{
if(billingResult.ResponseCode==BillingResponseCode.Ok)
{
//在此处获取产品列表并返回
}
}
}
供参考。我就是这样做的。这不是一个完整的代码,但这将给你和想法。
监听器-PCL
============
私有异步任务EventClicked()
{
var skuList=新列表();
添加(“[nameofsubscriptionfoundinyourgoogleplay]”;
if(等待\u billingClientLifecycle.Initialize(skuList,断开连接))
{
var firstProduct=_billingClientLifecycle?.ProductsInStore?.FirstOrDefault();
if(firstProduct!=null)
{
//在这里购买
}
}
}
私有void DisconnectedConnection()
{
//托多·艾尔丰,这里处理断开连接。。。
}
接口-PCL
===========
公共接口IInAppBillingMigratedNew
{
列出PurchasedProducts{get;set;}
列表产品存储{get;set;}
任务初始化(列表skuList,操作onDisconnected=null);
}
依赖平台机器人
===============
[程序集:XF.Dependency(typeof(inapplingmigratednew))]
AppBillingMigratedNew中的公共类:Java.Lang.Object,IBillingClientStateListener
,ISkuDetailsResponseListener,IInapplingMigratedNew
{
私有活动上下文=>CrossCurrentActivity.Current.Activity
?抛出新的NullReferenceException(“当前上下文/活动为null”);
私人账单客户(BillingClient);;
私有列表_skuList=新列表();
私有任务完成源\u已初始化;
私人行动-断开连接;
专用词典_skuswithskuadetails=新词典();
公共列表PurchasedProducts{get;set;}
公共列表产品存储{get;set;}
public IntPtr Handle=>抛出新的NotImplementedException();
公共任务初始化(列表skuList,操作disconnectedAction=null)
{
_disconnectedAction=disconnectedAction;
_tcsInitialized=new TaskCompletionSource();
var taskInit=_.Task;
_skuList=skuList;
_billingClient=billingClient.NewBuilder(上下文)
.SetListener(此)
.EnablePendingPurchases()
.Build();
如果(!\u billingClient.IsReady)
{
_billingClient.StartConnection(此);
}
返回taskInit;
}
#区域IBillingClientStateListener
公共无效OnBillingServiceDisconnected()
{
Console.WriteLine($“连接已断开”);
_tcsInitialized?.TrySetResult(false);
_disconnectedAction?.Invoke();
}
BillingSetupFinished(BillingResult BillingResult)上的公共无效
{
var responseCode=billingResult.responseCode;
var debugMessage=billingResult.debugMessage;
if(responseCode==BillingResponseCode.Ok)
{
QuerySkuDetails();
QueryPurchases();
_tcsInitialized?.TrySetResult(真);
}
其他的
{
WriteLine($“连接失败{debugMessage}”);
_tcsInitialized?.TrySetResult(false);
}
}
#端区
#区域ISkuDetailsResponseListener
公共无效OnSkuDetailsResponse(BillingResult BillingResult,IList skuDetailsList)
{
如果(billingResult==null)
{
Console.WriteLine(“onSkuDetailsResponse:null BillingResult”);
返回;
}
var responseCode=billingResult.responseCode;
var debugMessage=billingResult.debugMessage;
开关(响应代码)
{
案例BillingResponseCode。确定:
如果(skuDetailsList==null)
{
_skuswithskuadetails.Clear();
}
其他的
{
如果(skuDetailsList.Count>0)
{
ProductsInStore=新列表();
}
foreach(skuDetailsList中的变量skuDetails)
{
_skusWithSkuDetails。添加
public class SkuDetailsResponseListener : Java.Lang.Object, ISkuDetailsResponseListener
{
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skus)
{
if (billingResult.ResponseCode == BillingResponseCode.Ok)
{
// get list of Products here and return
}
}
}
FYI. This is how I did it. This is not a complete code but this will give you and idea.
Listener - PCL
============
private async Task EventClicked()
{
var skuList = new List<string>();
skuList.Add("[nameofsubscriptionfoundinyourgoogleplay]");
if (await _billingClientLifecycle.Initialize(skuList, DisconnectedConnection))
{
var firstProduct = _billingClientLifecycle?.ProductsInStore?.FirstOrDefault();
if (firstProduct != null)
{
//purchase here
}
}
}
private void DisconnectedConnection()
{
//Todo.alfon. handle disconnection here...
}
Interface - PCL
===========
public interface IInAppBillingMigratedNew
{
List<InAppBillingPurchase> PurchasedProducts { get; set; }
List<InAppBillingProduct> ProductsInStore { get; set; }
Task<bool> Initialize(List<String> skuList, Action onDisconnected = null);
}
Dependency - Platform Droid
===============
[assembly: XF.Dependency(typeof(InAppBillingMigratedNew))]
public class InAppBillingMigratedNew : Java.Lang.Object, IBillingClientStateListener
, ISkuDetailsResponseListener, IInAppBillingMigratedNew
{
private Activity Context => CrossCurrentActivity.Current.Activity
?? throw new NullReferenceException("Current Context/Activity is null");
private BillingClient _billingClient;
private List<string> _skuList = new List<string>();
private TaskCompletionSource<bool> _tcsInitialized;
private Action _disconnectedAction;
private Dictionary<string, SkuDetails> _skusWithSkuDetails = new Dictionary<string, SkuDetails>();
public List<InAppBillingPurchase> PurchasedProducts { get; set; }
public List<InAppBillingProduct> ProductsInStore { get; set; }
public IntPtr Handle => throw new NotImplementedException();
public Task<bool> Initialize(List<string> skuList, Action disconnectedAction = null)
{
_disconnectedAction = disconnectedAction;
_tcsInitialized = new TaskCompletionSource<bool>();
var taskInit = _tcsInitialized.Task;
_skuList = skuList;
_billingClient = BillingClient.NewBuilder(Context)
.SetListener(this)
.EnablePendingPurchases()
.Build();
if (!_billingClient.IsReady)
{
_billingClient.StartConnection(this);
}
return taskInit;
}
#region IBillingClientStateListener
public void OnBillingServiceDisconnected()
{
Console.WriteLine($"Connection disconnected.");
_tcsInitialized?.TrySetResult(false);
_disconnectedAction?.Invoke();
}
public void OnBillingSetupFinished(BillingResult billingResult)
{
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
if (responseCode == BillingResponseCode.Ok)
{
QuerySkuDetails();
QueryPurchases();
_tcsInitialized?.TrySetResult(true);
}
else
{
Console.WriteLine($"Failed connection {debugMessage}");
_tcsInitialized?.TrySetResult(false);
}
}
#endregion
#region ISkuDetailsResponseListener
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skuDetailsList)
{
if (billingResult == null)
{
Console.WriteLine("onSkuDetailsResponse: null BillingResult");
return;
}
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
switch (responseCode)
{
case BillingResponseCode.Ok:
if (skuDetailsList == null)
{
_skusWithSkuDetails.Clear();
}
else
{
if (skuDetailsList.Count > 0)
{
ProductsInStore = new List<InAppBillingProduct>();
}
foreach (var skuDetails in skuDetailsList)
{
_skusWithSkuDetails.Add(skuDetails.Sku, skuDetails);
//ToDo.alfon. make use mapper here
ProductsInStore.Add(new InAppBillingProduct
{
Name = skuDetails.Title,
Description = skuDetails.Description,
ProductId = skuDetails.Sku,
CurrencyCode = skuDetails.PriceCurrencyCode,
LocalizedIntroductoryPrice = skuDetails.IntroductoryPrice,
LocalizedPrice = skuDetails.Price,
MicrosIntroductoryPrice = skuDetails.IntroductoryPriceAmountMicros,
MicrosPrice = skuDetails.PriceAmountMicros
});
}
}
break;
case BillingResponseCode.ServiceDisconnected:
case BillingResponseCode.ServiceUnavailable:
case BillingResponseCode.BillingUnavailable:
case BillingResponseCode.ItemUnavailable:
case BillingResponseCode.DeveloperError:
case BillingResponseCode.Error:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
case BillingResponseCode.UserCancelled:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
// These response codes are not expected.
case BillingResponseCode.FeatureNotSupported:
case BillingResponseCode.ItemAlreadyOwned:
case BillingResponseCode.ItemNotOwned:
default:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
}
}
#endregion
#region Helper Methods Private
private void ProcessPurchases(List<Purchase> purchasesList)
{
if (purchasesList == null)
{
Console.WriteLine("No purchases done.");
return;
}
if (IsUnchangedPurchaseList(purchasesList))
{
Console.WriteLine("Purchases has not changed.");
return;
}
_purchases.AddRange(purchasesList);
PurchasedProducts = _purchases.Select(sku => new InAppBillingPurchase
{
PurchaseToken = sku.PurchaseToken
})?.ToList();
if (purchasesList != null)
{
LogAcknowledgementStatus(purchasesList);
}
}
private bool IsUnchangedPurchaseList(List<Purchase> purchasesList)
{
// TODO: Optimize to avoid updates with identical data.
return false;
}
private void LogAcknowledgementStatus(List<Purchase> purchasesList)
{
int ack_yes = 0;
int ack_no = 0;
foreach (var purchase in purchasesList)
{
if (purchase.IsAcknowledged)
{
ack_yes++;
}
else
{
ack_no++;
}
}
//Log.d(TAG, "logAcknowledgementStatus: acknowledged=" + ack_yes +
// " unacknowledged=" + ack_no);
}
private void QuerySkuDetails()
{
var parameters = SkuDetailsParams
.NewBuilder()
.SetType(BillingClient.SkuType.Subs)
.SetSkusList(_skuList)
.Build();
_billingClient.QuerySkuDetailsAsync(parameters, this);
}
private void QueryPurchases()
{
if (!_billingClient.IsReady)
{
Console.WriteLine("queryPurchases: BillingClient is not ready");
}
var result = _billingClient.QueryPurchases(BillingClient.SkuType.Subs);
ProcessPurchases(result?.PurchasesList?.ToList());
}
#endregion
}