2013-04-29 162 views
2

我的應用在購買應用內購買時有時崩潰。購買應用內購買應用時崩潰

大部分時間它工作正常,但有時應用程序崩潰沒有任何錯誤(我在調試模式下測試)。

要建立的應用程序內購買(非消耗品),我用下面的例子: 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 a.u.b.", 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(測試版)。現在我使用這個測試版,因爲它解決了我在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. 
    ================================================================= 

第二次更新

我剛剛發現出現問題的方式。購買和恢復應用程序內購買的按鈕以模態視圖顯示。當我重新打開模式視圖並點擊購買或恢復按鈕時,似乎應用程序崩潰。因此,當我第一次打開模式視圖並點擊購買和恢復按鈕時(大多數情況下)可以正常工作。但是,當我重新打開模式視圖,並點擊購買或恢復按鈕時,該應用程序崩潰,出現上述錯誤。

有人熟悉這個嗎?

回答

3

問題解決!我不得不在關閉視圖時刪除TransactionObserver。

public override void ViewWillDisappear (bool animated) 
    { 
     base.ViewWillDisappear (animated); 

     //Prevents crash when re-opening view 
     SKPaymentQueue.DefaultQueue.RemoveTransactionObserver (theObserver); 
    } 
2

好吧,這真是愚蠢。我會繼續前進,拿出一張郵票和一支鋼筆,向蒂姆庫克開始寫一封措辭強烈的信件。

我從經驗中發現SKProductsRequestDelegate.RequestFailed偶爾會返回空NSError。這會在您的方法的第一行中引起空引用異常。這很糟糕,我不確定它爲什麼會發生。

你可能你的代碼改成這樣:

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例子中得到了這個嗎?

+0

感謝您的回覆。我試過你說的,但不幸的是問題沒有解決。 – StackFlower 2013-05-01 10:18:16

+0

我剛發現問題出現的方式。看到我的第二個更新。 – StackFlower 2013-05-01 10:33:24

+0

好的,太好了。如果您將設備置於Flight Mode並嘗試購買,則會發生我的問題。類似的沒有細胞服務等 – jonathanpeppers 2013-05-01 11:50:51

1

我也有同樣的問題。我發現有

[[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; 

在viewDidUnload不起作用。它需要在viewWillDisappear中。這似乎已經解決了我的問題。