I have a mistake, and I'm losing my mind because it happens only in certain circumstances, but I really donβt understand when, perhaps, a more experienced eye can help me:
In Google tutorials, I took the code to make an in-app purchase, in short this code :
Initialize the In-App ServiceRetrieve the price objectStart the in-purchase if the user clicks the Buy button.
Now, in all the tests I did, this work is fine (more devices and APIs) BUT . I have a ton of reports that say: threw a NullPointerException on mHelper .
I think this happens in OnDestroy () when the service is configured, but I'm not sure, and I could not fix it (full error log at the end).
Here is my cleared and commented out maximum possible code:
IabHelper mHelper; IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener; Activity c; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_room); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); c=this; //Initialize the In-App Service mHelper = new IabHelper(this, "my_key"); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { if (!result.isSuccess()) { //Problem setting up In-app Billing return; } if (mHelper == null) return; //Ask for the price List additionalSkuList = new ArrayList(); additionalSkuList.add("SKU_ID"); try { mHelper.queryInventoryAsync(true, additionalSkuList, mQueryFinishedListener); }catch (Exception e){ //Fail while asking the price } } }); //Buy Button Listener b_buy.setOnClickListener(new View.OnClickListener() { public void onClick(View V) { try { String payload= "my_payload" mHelper.launchPurchaseFlow(c, "SKU_ID",1111,mPurchaseFinishedListener, payload); } catch (Exception e) { //Error launching purchase flow. Another async operation in progress } } }); // Callback for when a purchase is finished mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { public void onIabPurchaseFinished(IabResult result, Purchase purchase) { // if we were disposed of in the meantime, quit. if (mHelper == null) return; if (result.isFailure()) { //Error while buying return; } if (purchase.getSku().equals("SKU_ID")) { // bought the premium upgrade! } } }; } //For retrieve the price: IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { if (result.isFailure()) { return; } String z = inventory.getSkuDetails("SKU_ID").getPrice(); //The price of the object is + z !!! } }; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (mHelper == null) return; // Pass on the activity result to the helper for handling if (!mHelper.handleActivityResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onDestroy() { super.onDestroy(); if (mHelper != null) mHelper.dispose(); mHelper = null; } @Override public void onBackPressed() { super.onBackPressed(); finish(); } }
And this is a mistake:
Exception java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference com.myproj.util.IabHelper.startSetup (IabHelper.java) __null__.dispose (IabHelper.java) __null__.launchPurchaseFlow (IabHelper.java) __null__.handleActivityResult (IabHelper.java) __null__.queryInventory (IabHelper.java) __null__.queryInventoryAsync (IabHelper.java) __null__.getResponseDesc (IabHelper.java) __null__.checkSetupDone (IabHelper.java) __null__.getResponseCodeFromBundle (IabHelper.java) __null__.getResponseCodeFromIntent (IabHelper.java) __null__.queryPurchases (IabHelper.java) __null__.querySkuDetails (IabHelper.java) com.myproj.util.IabHelper.startSetup (IabHelper.java) __null__.dispose (IabHelper.java) __null__.launchPurchaseFlow (IabHelper.java) __null__.handleActivityResult (IabHelper.java) __null__.queryInventory (IabHelper.java) __null__.queryInventoryAsync (IabHelper.java) __null__.getResponseDesc (IabHelper.java) __null__.checkSetupDone (IabHelper.java) __null__.getResponseCodeFromBundle (IabHelper.java) __null__.getResponseCodeFromIntent (IabHelper.java) __null__.queryPurchases (IabHelper.java) __null__.querySkuDetails (IabHelper.java) com.myproj.util.IabHelper.startSetup (IabHelper.java) __null__.dispose (IabHelper.java) __null__.launchPurchaseFlow (IabHelper.java) __null__.handleActivityResult (IabHelper.java) __null__.queryInventory (IabHelper.java) __null__.queryInventoryAsync (IabHelper.java) __null__.getResponseDesc (IabHelper.java) __null__.checkSetupDone (IabHelper.java) __null__.getResponseCodeFromBundle (IabHelper.java) __null__.getResponseCodeFromIntent (IabHelper.java) __null__.queryPurchases (IabHelper.java) __null__.querySkuDetails (IabHelper.java) com.myproj.util.IabHelper$2.run (IabHelper.java) java.lang.Thread.run (Thread.java:818)
Here's some involved method of the Google IabHelper class (maybe you don't need to read this) . They are only mentioned in the error log and are written by Google:
Dispose:
public void dispose() { logDebug("Disposing."); mSetupDone = false; if (mServiceConn != null) { logDebug("Unbinding from service."); if (mContext != null) mContext.unbindService(mServiceConn); } mDisposed = true; mContext = null; mServiceConn = null; mService = null; mPurchaseListener = null; }
StartSetup:
public void startSetup(final OnIabSetupFinishedListener listener) { // If already set up, can't do it again. checkNotDisposed(); if (mSetupDone) throw new IllegalStateException("IAB helper is already set up."); // Connection to IAB service logDebug("Starting in-app billing setup."); mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { logDebug("Billing service disconnected."); mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { if (mDisposed) return; logDebug("Billing service connected."); mService = IInAppBillingService.Stub.asInterface(service); String packageName = mContext.getPackageName(); try { logDebug("Checking for in-app billing 3 support."); // check for in-app billing v3 support int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP); if (response != BILLING_RESPONSE_RESULT_OK) { if (listener != null) listener.onIabSetupFinished(new IabResult(response, "Error checking for billing v3 support.")); // if in-app purchases aren't supported, neither are subscriptions. mSubscriptionsSupported = false; return; } logDebug("In-app billing version 3 supported for " + packageName); // check for v3 subscriptions support response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS); if (response == BILLING_RESPONSE_RESULT_OK) { logDebug("Subscriptions AVAILABLE."); mSubscriptionsSupported = true; } else { logDebug("Subscriptions NOT AVAILABLE. Response: " + response); } mSetupDone = true; } catch (RemoteException e) { if (listener != null) { listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION, "RemoteException while setting up in-app billing.")); } e.printStackTrace(); return; } if (listener != null) { listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful.")); } } }; Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); serviceIntent.setPackage("com.android.vending"); if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0).isEmpty()) { // service available to handle that Intent mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); } else { // no service available to handle that Intent if (listener != null) { listener.onIabSetupFinished( new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing service unavailable on device.")); } } }
and IabHelper constructor:
public IabHelper(Context ctx, String base64PublicKey) { mContext = ctx.getApplicationContext(); mSignatureBase64 = base64PublicKey; logDebug("IAB helper created."); }
Here is the full IabHelper class.
Summary:
Tell me if you can see where / when the NullPointerException mHelper variable is mHelper . I could not test the virtual and physical devices from the code, as it works fine.
I am pretty sure that after that the function will be closed, but I do not understand why fix it.
Write if you need more information. Thanks everyone!