应用程序购买时,应用程序崩溃

有时购买应用程序内购买的东西,我的应用程序崩溃。

大多数情况下,它工作正常,但有时应用程序崩溃没有任何错误(我在debugging模式下testing)。

要设置应用内购买(非消费品),我使用了以下示例: https : //github.com/conceptdev/xamarin-samples/tree/master/InAppPurchase/NonConsumables

处理应用内购买的类看起来像这样:

public class InAppPurchaseManager : SKProductsRequestDelegate { public static NSString InAppPurchaseManagerProductsFetchedNotification = new NSString("InAppPurchaseManagerProductsFetchedNotification"); public static NSString InAppPurchaseManagerTransactionFailedNotification = new NSString("InAppPurchaseManagerTransactionFailedNotification"); public static NSString InAppPurchaseManagerTransactionSucceededNotification = new NSString("InAppPurchaseManagerTransactionSucceededNotification"); public static NSString InAppPurchaseManagerRequestFailedNotification = new NSString("InAppPurchaseManagerRequestFailedNotification"); SKProductsRequest productsRequest; CustomPaymentObserver theObserver; SKProduct[] products; public static NSAction Done {get;set;} public InAppPurchaseManager () { theObserver = new CustomPaymentObserver(this); SKPaymentQueue.DefaultQueue.AddTransactionObserver(theObserver); } // received response to RequestProductData - with price,title,description info public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response) { products = response.Products; NSDictionary userInfo = null; if (products.Length > 0) { NSObject[] productIdsArray = new NSObject[response.Products.Length]; NSObject[] productsArray = new NSObject[response.Products.Length]; for (int i = 0; i < response.Products.Length; i++) { productIdsArray[i] = new NSString(response.Products[i].ProductIdentifier); productsArray[i] = response.Products[i]; } userInfo = NSDictionary.FromObjectsAndKeys (productsArray, productIdsArray); } NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerProductsFetchedNotification,this,userInfo); foreach (string invalidProductId in response.InvalidProducts) { Console.WriteLine("Invalid product id: " + invalidProductId ); } } // request multiple products at once public void RequestProductData (List<string> productIds) { var array = new NSString[productIds.Count]; for (var i = 0; i < productIds.Count; i++) { array[i] = new NSString(productIds[i]); } NSSet productIdentifiers = NSSet.MakeNSObjectSet<NSString>(array); //set up product request for in-app purchase productsRequest = new SKProductsRequest(productIdentifiers); productsRequest.Delegate = this; // SKProductsRequestDelegate.ReceivedResponse productsRequest.Start(); Console.WriteLine ("BEREIKT"); } // Verify that the iTunes account can make this purchase for this application public bool CanMakePayments() { return SKPaymentQueue.CanMakePayments; } public void PurchaseProduct(string appStoreProductId) { Console.WriteLine("PurchaseProduct " + appStoreProductId); SKPayment payment = SKPayment.PaymentWithProduct (appStoreProductId); SKPaymentQueue.DefaultQueue.AddPayment (payment); } public void CompleteTransaction (SKPaymentTransaction transaction) { Console.WriteLine ("CompleteTransaction " + transaction.TransactionIdentifier); var productId = transaction.Payment.ProductIdentifier; // Register the purchase, so it is remembered for next time //PhotoFilterManager.Purchase(productId); UserDefaults.Purchase(productId); FinishTransaction (transaction, true); //Show Dialog new UIAlertView("Succes", "De aankoop is gelukt." + "\n Je kunt de gekozen categorieën nu spelen.", null, "OK", null).Show(); /* if (ReceiptValidation.VerificationController.SharedInstance.VerifyPurchase (transaction)) { Console.WriteLine ("Verified!"); // Register the purchase, so it is remembered for next time PhotoFilterManager.Purchase(productId); FinishTransaction (transaction, true); } else { Console.WriteLine ("NOT Verified :("); FinishTransaction (transaction, false); } */ } public void RestoreTransaction (SKPaymentTransaction transaction) { // Restored Transactions always have an 'original transaction' attached Console.WriteLine("RestoreTransaction " + transaction.TransactionIdentifier + "; OriginalTransaction " + transaction.OriginalTransaction.TransactionIdentifier); var productId = transaction.OriginalTransaction.Payment.ProductIdentifier; // Register the purchase, so it is remembered for next time //PhotoFilterManager.Purchase(productId); // it's as though it was purchased again UserDefaults.Purchase(productId); FinishTransaction(transaction, true); } public void FailedTransaction (SKPaymentTransaction transaction) { //SKErrorPaymentCancelled == 2 if (transaction.Error.Code == 2) // user cancelled Console.WriteLine("User CANCELLED FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription); else // error! Console.WriteLine("FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription); FinishTransaction(transaction,false); //Show Dialog new UIAlertView("Helaas", "De aankoop is mislukt." + "\n Probeer het op een later tijdstip nogmaals aub", null, "OK", null).Show(); } public void FinishTransaction(SKPaymentTransaction transaction, bool wasSuccessful) { Console.WriteLine("FinishTransaction " + wasSuccessful); // remove the transaction from the payment queue. SKPaymentQueue.DefaultQueue.FinishTransaction(transaction); // THIS IS IMPORTANT - LET'S APPLE KNOW WE'RE DONE !!!! using (var pool = new NSAutoreleasePool()) { NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {transaction},new NSObject[] {new NSString("transaction")}); if (wasSuccessful) { // send out a notification that we've finished the transaction NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerTransactionSucceededNotification,this,userInfo); } else { // send out a notification for the failed transaction NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerTransactionFailedNotification,this,userInfo); } } } /// <summary> /// Probably could not connect to the App Store (network unavailable?) /// </summary> public override void RequestFailed (SKRequest request, NSError error) { Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription); using (var pool = new NSAutoreleasePool()) { NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {error},new NSObject[] {new NSString("error")}); // send out a notification for the failed transaction NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerRequestFailedNotification,this,userInfo); } } /// <summary> /// Restore any transactions that occurred for this Apple ID, either on /// this device or any other logged in with that account. /// </summary> public void Restore() { Console.WriteLine (" ** InAppPurchaseManager Restore()"); // theObserver will be notified of when the restored transactions start arriving <- AppStore SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions(); } } 

什么可能导致崩溃?

仅供参考:我正在使用Xamarin iOS版本6.3.4.36(testing版)。 现在我使用这个testing版本,因为它解决了我与Game Center有关的问题。 Xamarin的稳定版本还没有解决这个问题。

PS。 我读了我用过的例子没有实现RECEIPT VERIFICATION。 这是什么意思,这是必要的实施?

第一次更新:

有时我得到这个错误。

  mono-rt: Stacktrace: mono-rt: at <unknown> <0xffffffff> mono-rt: at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x0009f, 0xffffffff> mono-rt: at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38 mono-rt: at PP_IOS.Application.Main (string[]) [0x00001] in /Users/Mac01/Projects/PP/PP_IOS/Main.cs:19 mono-rt: at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00050, 0xffffffff> mono-rt: Native stacktrace: mono-rt: ================================================================= Got a SIGSEGV while executing native code. This usually indicates a fatal error in the mono runtime or one of the native libraries used by your application. ================================================================= 

而另一次我得到这个错误:

  mono-rt: Stacktrace: mono-rt: at <unknown> <0xffffffff> mono-rt: at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging.void_objc_msgSend_IntPtr (intptr,intptr,intptr) <IL 0x00025, 0xffffffff> mono-rt: at MonoTouch.StoreKit.SKPaymentQueue.AddPayment (MonoTouch.StoreKit.SKPayment) [0x0001c] in /Developer/MonoTouch/Source/monotouch/src/StoreKit/SKPaymentQueue.g.cs:107 mono-rt: at PP_IOS.InAppPurchaseManager.PurchaseProduct (string) [0x0001f] in /Users/Mac01/Projects/PP/PP_IOS/Utils/InAppPurchase/InAppPurchaseManager.cs:109 mono-rt: at PP_IOS.UpgradeScreen.<BuyCategoryArtistsAndSports>m__21 () [0x0003d] in /Users/Mac01/Projects/PP/PP_IOS/ControllersUniversal/UpgradeScreen.cs:171 mono-rt: at MonoTouch.Foundation.NSAsyncActionDispatcher.Apply () [0x00000] in /Developer/MonoTouch/Source/monotouch/src/shared/Foundation/NSAction.cs:87 mono-rt: at (wrapper runtime-invoke) object.runtime_invoke_void__this__ (object,intptr,intptr,intptr) <IL 0x0004e, 0xffffffff> mono-rt: at <unknown> <0xffffffff> mono-rt: at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x0009f, 0xffffffff> mono-rt: at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38 mono-rt: at PP_IOS.Application.Main (string[]) [0x00001] in /Users/Mac01/Projects/PP/PP_IOS/Main.cs:19 mono-rt: at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00050, 0xffffffff> mono-rt: Native stacktrace: mono-rt: ================================================================= Got a SIGSEGV while executing native code. This usually indicates a fatal error in the mono runtime or one of the native libraries used by your application. ================================================================= 

第二次更新

我刚刚发现问题出现的方式。 购买和恢复应用内购买的button以模式视图显示。 当我重新打开模式视图并点击购买或恢复button时,应用似乎崩溃了。 所以当我第一次打开模式视图,并点击购买和恢复button(大部分时间)工作正常。 但是,当我重新打开模式视图,并点击购买或恢复button,该应用程序崩溃与上面显示的错误。

有人熟悉这个?

问题解决了!

closures视图时,我不得不删除TransactionObserver。

  public override void ViewWillDisappear (bool animated) { base.ViewWillDisappear (animated); //Prevents crash when re-opening view SKPaymentQueue.DefaultQueue.RemoveTransactionObserver (theObserver); } 

好吧,这真是愚蠢。 我会继续前进,拿出一张邮票和钢笔,给蒂姆·库克开始写一封措辞强硬的信。

我从经验中发现, SKProductsRequestDelegate.RequestFailed可以偶尔返回一个null NSError 。 这将导致您的方法的第一行上的空引用exception。 这是非常可怕的,我不知道为什么会发生。

您可能会将您的代码更改为:

 public override void RequestFailed (SKRequest request, NSError error) { if (error == null) Console.WriteLine("NSError is null!"); else Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription); using (var pool = new NSAutoreleasePool()) { NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {error},new NSObject[] {new NSString("error")}); // send out a notification for the failed transaction NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerRequestFailedNotification,this,userInfo); } } 

确保你也在NSNotificationCenter的另一端进行了考虑。

顺便说一句,我不知道什么NSAutoreleasePool爵士乐是什么 – 你应该删除它。 你从一个非常古老的MonoTouch例子中得到了这个吗?

我也有同样的问题。 我发现有

 [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; 

在viewDidUnload不起作用。 它需要在viewWillDisappear。 这似乎已经解决了我的问题。