Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android In-app purchase NullPointerException

I got an error and I'm going mad because happen only in some circumstances but I really don't understand when, maybe a more expert eye can help me:

From the Google tutorials I took the code for make an in-app purchase, briefly this code do:

  1. Initialize the In-App Service
  2. Retrieve the price of the object
  3. Start the in-purchase if the user click the button "Buy".

Now, in all the tests that I did this work fine (more devices and API) BUT I got ton of reports that say: thrown a NullPointerException on mHelper.

I think that this happen in on the OnDestroy() when the service is disposed but i'm not sure and I couldn't fix it(Complete Error Log in the end).

Here is my cleaned and commented as much as 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 the error:

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 is some involved method of the Google IabHelper class (Probably you'll NOT need to read this) They are just 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 the Constructor of IabHelper:

    public IabHelper(Context ctx, String base64PublicKey) {
        mContext = ctx.getApplicationContext();
        mSignatureBase64 = base64PublicKey;
        logDebug("IAB helper created.");
    }

Here is the complete IabHelper class.

Recap:

Tell me if you can see where/when the NullPointerException of the variable mHelper is thrown. I couldn't from the code and test with virtual and physical devices since work fine.

I'm pretty much sure that the error is thrown after that the Activity is closed but I don't understand why for fix it.

Write if you need more info. Thank to all!

like image 297
GMX Avatar asked Oct 30 '22 13:10

GMX


1 Answers

You should try to avoid using getApplicationContext() as much as possible, as this will highly increase the chance of getting Force Closes.

Use Activity context. If you are create object of IabHelper in Activity then Pass ActivityName.this(means Activity context).

If you are in Fragment the use getActivity().

And in IabHelper class use like this::

public IabHelper(Context ctx, String base64PublicKey) {
    mContext = ctx;
    mSignatureBase64 = base64PublicKey;
    logDebug("IAB helper created.");
}

I hope it works fine.

like image 185
Vijay Makwana Avatar answered Nov 09 '22 05:11

Vijay Makwana